Join GitHub today
GitHub is home to over 28 million developers working together to host and review code, manage projects, and build software together.
Sign upUse `RowCons` for child components #439
Comments
natefaubion
added
the
idea
label
Apr 7, 2017
This comment has been minimized.
This comment has been minimized.
|
We could also add the component |
This comment has been minimized.
This comment has been minimized.
|
Reimplementing queries with rows could use open variant abstraction, that can be extracted to different library |
garyb
added this to the next milestone
Sep 13, 2017
garyb
added
the
enhancement
label
Sep 13, 2017
garyb
referenced this issue
Sep 13, 2017
Open
`Inject` style mechanism for choosing child slots implicitly #478
This comment has been minimized.
This comment has been minimized.
|
To elaborate more on the implementation: Currently child component state is managed in a single This is why If we start with an empty data decl to represent a slot, we can use this in rows: -- Indexed by query, output message, and slot key
data Slot (g :: Type -> Type) o kThen we can define slots for our components as something like: type ChildSlots =
( navigation :: Slot NavQuery NavMessage Unit
, item :: Slot ItemQuery ItemMessage String
)A nice pattern might be components exporting a partially applied slot: type NavigationSlot = Slot NavQuery NavMessage
type ItemSlot = Slot ItemQuery ItemMessage
type ChildSlots =
( navigation :: NavigationSlot Unit
, item :: ItemSlot String
)Which is relatively little boilerplate compared to the existing setup. Then we need to defined what our storage interface looks like. -- Storage is parameterized by slot cells
data SlotStorage (slot :: ((Type -> Type) -> Type -> Type -> Type) (# Type)That is, some storage indexed by child slots with values of some abstract slot which has the same signature as data DriverSlot h r eff g o kAnd then parameterize our storage: type DriverStorage h r eff = SlotStorage (DriverSlot h r eff)However, when constructing our component we need to defer instantiation of the data SlotStorageConstructor (# Type)
emptySlotStorage :: forall slot cs. SlotStorageConstructor cs -> SlotStorage slot csAnd then change the type Component' h s f (cs :: # Type) i o m =
{ initialState :: i -> s
, render :: s -> h (ComponentSlot h cs m (f Unit)) (f Unit)
, eval :: f ~> HalogenM s f cs o m
, receiver :: i -> Maybe (f Unit)
, initializer :: Maybe (f Unit)
, finalizer :: Maybe (f Unit)
, storage :: SlotStorageConstructor cs
}Where previously we were capturing the class BuildSlotStorage (cs :: # Type) where
slotStorageConstructor :: RProxy cs -> SlotStorageConstructor csWe could muster one up while building Then in the driver, we could use the constructor to initialize it, and given a map-like interface: lookupSlot
:: forall sym cx cs slot g o k
. RowCons sym (Slot g o k) cx cs
=> IsSymbol sym
=> Ord k
=> SProxy sym
-> k
-> SlotStorage slot cs
-> Maybe (slot g o k)
popSlot
:: forall sym cx cs slot g o k
. RowCons sym (Slot g o k) cx cs
=> IsSymbol sym
=> Ord k
=> SProxy sym
-> k
-> SlotStorage slot cs
-> Maybe (Tuple (slot f o k) (SlotStorage slot cs))
insertSlot
:: forall sym cx cs slot g o slot
. RowCons sym (Slot g o k) cx cs
=> IsSymbol sym
=> Ord k
=> SProxy sym
-> k
-> slot g o k
-> SlotStorage slot cs
-> SlotStorage slot csWe could manage state as we do now. The difficult part then is capturing all these constraints. Since Halogen uses an initial encoding for everything, we have to put these dictionaries in the appropriate constructors ( So given a signature for query
∷ ∀ s f sym cx cs g o k m a
. RowCons sym (Slot g o k) cx cs a
⇒ IsSymbol sym
⇒ Ord k
⇒ SProxy sym
→ k
→ g a
→ HalogenM s f cs m (Maybe a)We would have to put these constraints in the |
This comment has been minimized.
This comment has been minimized.
|
One option is to always bundle up the partially applied operations, polymorphic over the slot type. newtype SlotOperations (cs :: # Type) g o k = SlotOperations
{ lookup :: forall slot. SlotStorage slot cs -> slot g o k
, pop :: forall slot. k -> SlotStorage slot cs -> Maybe (Tuple (slot g o k) (SlotStorage slot cs))
, insert :: forall slot. k -> slot g o k -> SlotStorage slot cs -> SlotStorage slot cs
}data ComponentSlot h (cs :: # Type) m a = forall g o k i. ComponentSlot (SlotOperations cs g o k) (Component h g i o m) i (o -> a) |
natefaubion commentedApr 7, 2017
With
RowCons, it's possible to define a mapping for child components without all the coproduct/either cruft. This should be more efficient and ergonomic because child queries and state can be dispatched directly without any wrapping.Internally, we could build a record based on this set of rows, with individual
Maps for each component type.This is great because we can define the Slot -> Query mapping together in a single definition. We had originally wanted to do this with TC magic and type-level assoc lists, but it was quite hideous.