/
Slot.purs
140 lines (123 loc) · 4.06 KB
/
Slot.purs
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
module Halogen.Data.Slot
( Slot
, SlotStorage
, empty
, lookup
, insert
, pop
, slots
, foreachSlot
) where
import Prelude
import Data.Foldable (traverse_)
import Data.Map (Map)
import Data.Map as Map
import Data.Maybe (Maybe(..))
import Data.Monoid.Alternate (Alternate(..))
import Data.Newtype (un)
import Data.Symbol (class IsSymbol, reflectSymbol)
import Data.Tuple (Tuple(..))
import Halogen.Data.OrdBox (OrdBox, mkOrdBox, unOrdBox)
import Prim.Row as Row
import Type.Proxy (Proxy)
import Unsafe.Coerce (unsafeCoerce)
foreign import data Any :: Type
-- | A type which records the queries, output messages, and slot identifier for
-- | a particular slot (ie. a location in HTML where a component is rendered).
-- | For example:
-- |
-- | ```purescript
-- | type ButtonSlot slot = Slot Button.Query Button.Output slot
-- |
-- | -- A component using this slot type can have one type of child component,
-- | -- which supports `Button.Query` queries, `Button.Output` outputs, and
-- | -- which can be uniquely identified by an integer.
-- | type Slots = ( button :: ButtonSlot Int )
-- | ```
-- |
-- | - `query` represents the requests that can be made of this component
-- | - `output` represents the output messages that can be raised by this component
-- | - `slot` represents the unique identifier for this component
data Slot :: (Type -> Type) -> Type -> Type -> Type
data Slot (query :: Type -> Type) output slot
newtype SlotStorage (slots :: Row Type) (slot :: (Type -> Type) -> Type -> Type) =
SlotStorage (Map (Tuple String (OrdBox Any)) Any)
empty :: forall slots slot. SlotStorage slots slot
empty = SlotStorage Map.empty
lookup
:: forall sym px slots slot query output s
. Row.Cons sym (Slot query output s) px slots
=> IsSymbol sym
=> Ord s
=> Proxy sym
-> s
-> SlotStorage slots slot
-> Maybe (slot query output)
lookup sym key (SlotStorage m) =
coerceSlot (Map.lookup (Tuple (reflectSymbol sym) (coerceBox (mkOrdBox key))) m)
where
coerceSlot :: Maybe Any -> Maybe (slot query output)
coerceSlot = unsafeCoerce
coerceBox :: OrdBox s -> OrdBox Any
coerceBox = unsafeCoerce
pop
:: forall sym px slots slot query output s
. Row.Cons sym (Slot query output s) px slots
=> IsSymbol sym
=> Ord s
=> Proxy sym
-> s
-> SlotStorage slots slot
-> Maybe (Tuple (slot query output) (SlotStorage slots slot))
pop sym key (SlotStorage m) =
coercePop (Map.pop (Tuple (reflectSymbol sym) (coerceBox (mkOrdBox key))) m)
where
coercePop :: Maybe (Tuple Any (Map (Tuple String (OrdBox Any)) Any)) -> Maybe (Tuple (slot query output) (SlotStorage slots slot))
coercePop = unsafeCoerce
coerceBox :: OrdBox s -> OrdBox Any
coerceBox = unsafeCoerce
insert
:: forall sym px slots slot query output s
. Row.Cons sym (Slot query output s) px slots
=> IsSymbol sym
=> Ord s
=> Proxy sym
-> s
-> slot query output
-> SlotStorage slots slot
-> SlotStorage slots slot
insert sym key val (SlotStorage m) =
SlotStorage (Map.insert (Tuple (reflectSymbol sym) (coerceBox (mkOrdBox key))) (coerceVal val) m)
where
coerceBox :: OrdBox s -> OrdBox Any
coerceBox = unsafeCoerce
coerceVal :: slot query output -> Any
coerceVal = unsafeCoerce
slots
:: forall sym px slots slot query output s
. Row.Cons sym (Slot query output s) px slots
=> IsSymbol sym
=> Ord s
=> Proxy sym
-> SlotStorage slots slot
-> Map s (slot query output)
slots sym (SlotStorage m) = un Alternate $ Map.foldSubmap Nothing Nothing go m
where
key = reflectSymbol sym
go (Tuple key' ob) val
| key == key' = Alternate $ Map.singleton (unOrdBox (coerceBox ob)) (coerceVal val)
| otherwise = Alternate Map.empty
coerceBox :: OrdBox Any -> OrdBox s
coerceBox = unsafeCoerce
coerceVal :: Any -> slot query output
coerceVal = unsafeCoerce
foreachSlot
:: forall m slots slot
. Applicative m
=> SlotStorage slots slot
-> (forall query output. slot query output -> m Unit)
-> m Unit
foreachSlot (SlotStorage m) k = traverse_ (k <<< coerceVal) m
where
coerceVal :: forall query output. Any -> slot query output
coerceVal = unsafeCoerce