-
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathOptional.hs
104 lines (89 loc) · 2.31 KB
/
Optional.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
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE NoImplicitPrelude #-}
module Optional where
import qualified Control.Applicative as A
import qualified Control.Monad as M
import Core
import qualified Prelude as P
-- | The `Optional` data type contains 0 or 1 value.
--
-- It might be thought of as a list, with a maximum length of one.
data Optional a
= Full a
| Empty
deriving stock (Eq, Show)
-- | Return the possible value if it exists; otherwise, the first argument.
--
-- >>> fullOr 99 (Full 8)
-- 8
--
-- >>> fullOr 99 Empty
-- 99
fullOr :: a -> Optional a -> a
fullOr x y = case y of
Empty -> x
Full z -> z
-- | Map the given function on the possible value.
--
-- >>> mapOptional (+1) Empty
-- Empty
--
-- >>> mapOptional (+1) (Full 8)
-- Full 9
mapOptional :: (a -> b) -> Optional a -> Optional b
mapOptional f = bindOptional (Full . f)
-- | Bind the given function on the possible value.
--
-- >>> bindOptional Full Empty
-- Empty
--
-- >>> bindOptional (\n -> if even n then Full (n - 1) else Full (n + 1)) (Full 8)
-- Full 7
--
-- >>> bindOptional (\n -> if even n then Full (n - 1) else Full (n + 1)) (Full 9)
-- Full 10
bindOptional :: (a -> Optional b) -> Optional a -> Optional b
bindOptional f x = case x of
Empty -> Empty
Full y -> f y
-- | Try the first optional for a value. If it has a value, use it; otherwise,
-- use the second value.
--
-- >>> Full 8 <+> Empty
-- Full 8
--
-- >>> Full 8 <+> Full 9
-- Full 8
--
-- >>> Empty <+> Full 9
-- Full 9
--
-- >>> Empty <+> Empty
-- Empty
(<+>) :: Optional a -> Optional a -> Optional a
(<+>) x y = case x of
Empty -> y
_ -> x
-- | Replaces the Full and Empty constructors in an optional.
--
-- >>> optional (+1) 0 (Full 8)
-- 9
--
-- >>> optional (+1) 0 Empty
-- 0
optional :: (a -> b) -> b -> Optional a -> b
optional f x y = fullOr x (mapOptional f y)
applyOptional :: Optional (a -> b) -> Optional a -> Optional b
applyOptional f a = bindOptional (`mapOptional` a) f
twiceOptional :: (a -> b -> c) -> Optional a -> Optional b -> Optional c
twiceOptional f = applyOptional . mapOptional f
contains :: (Eq a) => a -> Optional a -> Bool
contains _ Empty = False
contains a (Full z) = a == z
instance P.Functor Optional where
fmap = M.liftM
instance A.Applicative Optional where
(<*>) = M.ap
pure = Full
instance P.Monad Optional where
(>>=) = flip bindOptional