-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathAlternative.hs
164 lines (148 loc) · 4.4 KB
/
Alternative.hs
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
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE RebindableSyntax #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Alternative where
import Applicative (Applicative)
import qualified Applicative as A
import List (List (..))
import qualified List as L
import Optional (Optional (..))
import qualified Optional as O
import Parser (ParseResult (..), Parser)
import qualified Parser as P
import Prelude ((.))
-- | All instances of the `Alternative` type-class must satisfy three laws.
-- These laws are not checked by the compiler. These laws are given as:
--
-- * The law of left identity
-- `∀x. empty <|> x = x`
--
-- * The law of right identity
-- `∀x. x <|> empty = x`
--
-- * The law of associativity
-- `∀u v w. u <|> (v <|> w) = (u <|> v) <|> w`
--
-- You may notice that these are the same laws as Monoid. An alternative
-- can be considered a "monoid on applicative functors". The key difference
-- between the two classes is that Alternative is higher-kinded, meaning that
-- the type variable @k@ itself takes a type parameter.
-- The Alternative instance for @k@ is often distinct from any Monoid instance
-- for @k a@.
-- An Alternative instance should relate to the Applicative instance in some
-- way, although the exact relation required is an open question in the community.
-- Informally, it should be some kind of choice or alternation. Attempts to give
-- laws relating the Applicative and Alternative are discussed here:
-- https://wiki.haskell.org/Typeclassopedia#Laws_6
class (Applicative k) => Alternative k where
zero :: k a
(<|>) :: k a -> k a -> k a
infixl 3 <|>
-- | Return the first full Optional.
--
-- >>> Full 3 <|> zero
-- Full 3
--
-- >>> zero <|> Full 4
-- Full 4
--
-- >>> Full 3 <|> Full 4
-- Full 3
instance Alternative Optional where
zero :: Optional a
zero = Empty
(<|>) :: Optional a -> Optional a -> Optional a
(<|>) = (O.<+>)
-- | Append the lists.
-- This instance views lists as a non-deterministic choice between elements,
-- so the way we "alternate" them is to append the lists.
--
-- >>> 3 :. 4 :. 5 :. Nil <|> Nil
-- [3,4,5]
--
-- >>> Nil <|> 6 :. 7 :. 8 :. Nil
-- [6,7,8]
--
-- >>> 3 :. 4 :. 5 :. Nil <|> 6 :. 7 :. 8 :. Nil
-- [3,4,5,6,7,8]
instance Alternative List where
zero :: List a
zero = Nil
(<|>) :: List a -> List a -> List a
(<|>) = (L.++)
-- | Choose the first succeeding parser
--
-- /Tip:/ Check Parser.hs
--
-- >>> parse (character <|> valueParser 'v') ""
-- Result >< 'v'
--
-- >>> parse (constantParser UnexpectedEof <|> valueParser 'v') ""
-- Result >< 'v'
--
-- >>> parse (character <|> valueParser 'v') "abc"
-- Result >bc< 'a'
--
-- >>> parse (constantParser UnexpectedEof <|> valueParser 'v') "abc"
-- Result >abc< 'v'
instance Alternative Parser where
zero :: Parser a
zero = P.constantParser UnexpectedEof
(<|>) :: Parser a -> Parser a -> Parser a
(<|>) = (P.|||)
-- | Run the provided Alternative action zero or more times, collecting
-- a list of the results.
--
-- /Tip:/ Use @some@, @pure@ and @(<|>)@.
--
-- >>> parse (many character) ""
-- Result >< ""
--
-- >>> parse (many digit) "123abc"
-- Result >abc< "123"
--
-- >>> parse (many digit) "abc"
-- Result >abc< ""
--
-- >>> parse (many character) "abc"
-- Result >< "abc"
--
-- >>> parse (many (character *> valueParser 'v')) "abc"
-- Result >< "vvv"
--
-- >>> parse (many (character *> valueParser 'v')) ""
-- Result >< ""
-- Recursive definition. It's impossible to implement
-- otherwise without knowing the specialized type of k,
-- since we need to check for the zero element.
many :: (Alternative k) => k a -> k (List a)
many = (<|> A.pure Nil) . some
-- | Run the provided Alternative action one or more times, collecting
-- a list of the results.
--
-- /Tip:/ Use @(:.)@ and @many@.
--
-- >>> parse (some (character)) "abc"
-- Result >< "abc"
--
-- >>> parse (some (character *> valueParser 'v')) "abc"
-- Result >< "vvv"
--
-- >>> isP.errorResult (parse (some (character *> valueParser 'v')) "")
-- True
some :: (Alternative k) => k a -> k (List a)
some ka = A.lift2 (:.) ka (many ka)
-- | Combine a list of alternatives
--
-- >>> aconcat (Nil :: List (List Int))
-- []
--
-- >>> aconcat ((3:.4:.Nil) :. Nil :. (5:.6:.Nil) :. Nil
-- [3,4,5,6]
-- >>> aconcat (Empty :. Empty :. Full 7 :. Empty :. Full 8 :. Empty :. Nil)
-- Full 7
--
-- /Note:/ In the standard library, this function is called @asum@
aconcat :: (Alternative k) => List (k a) -> k a
aconcat = L.foldLeft (<|>) zero