File: Operations.hs

package info (click to toggle)
haskell-arrows 0.2-3
  • links: PTS
  • area: main
  • in suites: etch, etch-m68k
  • size: 152 kB
  • ctags: 3
  • sloc: haskell: 664; makefile: 60; sh: 22
file content (171 lines) | stat: -rw-r--r-- 5,806 bytes parent folder | download | duplicates (2)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
{-# OPTIONS_GHC -fglasgow-exts #-}

-----------------------------------------------------------------------------
-- |
-- Module      :  Control.Arrow.Operations
-- Copyright   :  (c) Ross Paterson 2003
-- License     :  BSD-style (see the LICENSE file in the distribution)
--
-- Maintainer  :  ross@soi.city.ac.uk
-- Stability   :  experimental
-- Portability :  non-portable (multi-parameter type classes)
--
-- Subclasses of 'Arrow' providing additional operations.
--
-- The signatures are designed to be compatible with the proposed
-- notation for arrows, cf. <http://www.haskell.org/arrows/>.

module Control.Arrow.Operations (
		-- * Conventions
		-- $conventions

		-- * State transformers
		ArrowState(..),
		-- * State readers
		ArrowReader(..),
		-- * Monoid writers
		ArrowWriter(..),
		-- * Errors
		ArrowError(..),
		tryInUnlessDefault,
		-- * Synchronous circuits
		ArrowCircuit(..),
	) where

import Control.Arrow
import Data.Monoid

-- $conventions
-- The arrow classes defined in this module have names like @Arrow@/Foo/,
-- and contain operations specific to such arrows.  Some of these include
-- a method @new@/Foo/, which maps computations to computations of the
-- same arrow type, but exposing some of the internals of the arrow.
--
-- Arrow transformers have names like /Bar/@Arrow@, and are
-- instances of appropriate arrow classes.  For each arrow
-- transformer, there is typically an encapsulation operator
-- @run@/Bar/ that removes that transformer from the outside of an
-- arrow type.  The 'Control.Arrow.Transformer.lift' method of the
-- 'Control.Arrow.Transformer.ArrowTransformer' class adds an arrow
-- transformer to the outside of an arrow type.
--
-- Typically a composite arrow type is built by applying a series of arrow
-- transformers to a base arrow (usually either a function arrow or a
-- 'Kleisli' arrow.  The 'Control.Arrow.Transformer.lift' method and the
-- @run@/Bar/ function operate only on the arrow transformer at the top
-- of this stack.  For more sophisticated manipulation of this stack of
-- arrow transformers, many arrow transformers provide an @ArrowAdd@/Bar/
-- class, with methods methods @lift@/Bar/ and @elim@/Bar/ to add and remove
-- the transformer anywhere in the stack.

-- | An arrow type that provides a read-only state (an environment).
-- If you also need to modify the state, use 'ArrowState'.

class Arrow a => ArrowReader r a | a -> r where
	-- | Obtain the current value of the state.
	readState :: a b r

	-- | Run a subcomputation in the same arrow, but with a different
	-- environment.  The environment of the outer computation is
	-- unaffected.
	--
	-- Typical usage in arrow notation:
	--
	-- >	proc p -> ...
	-- >		(|newReader cmd|) env

	newReader :: a e b -> a (e,r) b

-- | An arrow type that provides a modifiable state,
-- based of section 9 of /Generalising Monads to Arrows/, by John Hughes,
-- /Science of Computer Programming/ 37:67-111, May 2000.

class Arrow a => ArrowState s a | a -> s where
	-- | Obtain the current value of the state.
	fetch :: a e s
	-- | Assign a new value to the state.
	store :: a s ()

-- | An arrow type that collects additional output (of some 'Monoid' type).

class (Monoid w, Arrow a) => ArrowWriter w a | a -> w where
	-- | Add a piece of additional output.
	write :: a w ()

	-- | Run a subcomputation in the same arrow, making its additional
	-- output accessible.
	--
	-- Typical usage in arrow notation:
	--
	-- >	proc p -> do
	-- >		...
	-- >		(value, output) <- (|newWriter cmd|)

	newWriter :: a e b -> a e (b,w)

-- | An arrow type that includes errors (or exceptions).
--
-- Minimal definition: 'raise' and 'tryInUnless'.
--
-- /TODO:/ the operations here are inconsistent with other arrow transformers.

class Arrow a => ArrowError ex a | a -> ex where
	-- | Raise an error.
	raise :: a ex b

	-- | Traditional exception construct.
	--
	-- Typical usage in arrow notation:
	--
	-- >	proc p -> ...
	-- >		body `handle` \ex -> handler

	handle :: a e b		-- ^ computation that may raise errors
		-> a (e,ex) b	-- ^ computation to handle errors
		-> a e b
	handle f h = tryInUnless f (arr snd) h

	-- | Exception construct in the style of /Exceptional Syntax/,
	-- by Nick Benton and Andrew Kennedy, /JFP/ 11(4):395-410, July 2001.
	--
	-- Typical usage in arrow notation:
	--
	-- >	proc p -> ...
	-- >		(|tryInUnless
	-- >			body
	-- >			(\res -> success)
	-- >			(\ex -> handler)
	-- >		|)
	tryInUnless :: a e b	-- ^ computation that may raise errors
		-> a (e,b) c	-- ^ computation to receive successful results
		-> a (e,ex) c	-- ^ computation to handle errors
		-> a e c

	-- | Handler that returns the error as a value.
	newError :: a e b -> a e (Either ex b)
	newError f = handle (f >>> arr Right) (arr (Left . snd))

-- | A suitable value for 'tryInUnless' when the arrow type belongs to
-- 'ArrowChoice'.  To use it, you must define either 'handle' or 'newError'.

tryInUnlessDefault :: (ArrowError ex a, ArrowChoice a) =>
		a e b		-- ^ computation that may raise errors
		-> a (e,b) c	-- ^ computation to receive successful results
		-> a (e,ex) c	-- ^ computation to handle errors
		-> a e c
tryInUnlessDefault f s h = arr id &&& newError f >>> arr dist >>> h ||| s
	where	dist (e, Left ex) = Left (e, ex)
		dist (e, Right b) = Right (e, b)

-- tryInUnless (and thus handle) could be replaced by newError if:
-- 1. When ArrowChoice is available, tryInUnless and newError are equivalent.
-- 2. When tryInUnless is available, so is ArrowChoice.
--    (Counterexample: general CoKleisli)

-- | An arrow type that can be used to interpret synchronous circuits.

class ArrowLoop a => ArrowCircuit a where
	-- | A delay component.
	delay :: b		-- ^ the value to return initially.
		-> a b b	-- ^ an arrow that propagates its input
				-- with a one-tick delay.