module FRP.Grapefruit.Record (
Record (build),
X (X),
(:&) ((:&)),
(:::) ((::=), (::~)),
Cat,
cat,
Subrecord (narrow),
SignalRecord,
SignalStyle,
ConsumerRecord,
ProducerRecord,
ConnectorRecord,
ConnectorStyle,
consume,
produce
) where
import Control.Arrow as Arrow
import Data.TypeLevel.Bool
import Data.TypeEq
import FRP.Grapefruit.Circuit as Circuit
import FRP.Grapefruit.Signal as Signal (Of, Consumer, Producer)
import qualified FRP.Grapefruit.Signal as Signal
infixl 2 :&
infix 3 :::, ::=, ::~
class Record record where
build :: thing X
-> (forall record name signal val. (Record record) =>
thing record -> thing (record :& name ::: signal `Of` val))
-> thing record
instance Record X where
build nilThing _ = nilThing
instance (Record record) => Record (record :& name ::: signal `Of` val) where
build nilThing consThing = consThing (build nilThing consThing)
data X style = X
data (record :& field) style = !(record style) :& !(field style)
data family (name ::: signalOfVal) style :: *
undefinedName :: (name ::: val) style -> name
undefinedName = undefined
type family Cat (record1 :: * -> *) (record2 :: * -> *) :: * -> *
type instance Cat record1 X = record1
type instance Cat record1 (record2 :& field2) = Cat record1 record2 :& field2
cat :: (Record record1, Record record2) =>
record1 style -> record2 style -> Cat record1 record2 style
cat record1 = case build (nilCatThing record1) consCatThing of CatThing attach -> attach
newtype CatThing style record1 record2 = CatThing (record2 style -> Cat record1 record2 style)
nilCatThing :: record1 style -> CatThing style record1 X
nilCatThing record1 = CatThing $ \X -> record1
consCatThing :: CatThing style record1 record2
-> CatThing style record1 (record2 :& name ::: val)
consCatThing (CatThing attach) = CatThing $ \(record2 :& field2) -> attach record2 :& field2
class (Record subrecord, Record record) => Subrecord subrecord record where
narrow :: record style -> subrecord style
instance (Record record) => Subrecord X record where
narrow _ = X
instance (Dissection record remainder subname subsignal subval,
Subrecord subrecord remainder) =>
Subrecord (subrecord :& subname ::: subsignal `Of` subval) record where
narrow record = narrow remainder :& lookupField where
(remainder,lookupField) = dissect record
class (Record record, Record remainder) =>
Dissection record remainder lookupName lookupSignal lookupVal
| record lookupName -> remainder where
dissect :: record style
-> (remainder style,(lookupName ::: lookupSignal `Of` lookupVal) style)
instance (Present lookupName, Record remainder) =>
Dissection X remainder lookupName lookupSignal lookupVal where
dissect = undefined
instance (TypeEq name lookupName namesAreEq,
NameMatchDep namesAreEq
record name signal val
remainder lookupName lookupSignal lookupVal) =>
Dissection (record :& name ::: signal `Of` val)
remainder lookupName lookupSignal lookupVal where
dissect (record :& field) = (remainder,lookupField) where
(remainder,lookupField) = nameMatchDepExtract (typeEq name lookupName) record field
name = undefinedName field
lookupName = undefinedName lookupField
class (Record record, Record remainder) =>
NameMatchDep namesAreEq record name signal val remainder lookupName lookupSignal lookupVal
| namesAreEq record name signal val lookupName -> remainder where
nameMatchDepExtract :: namesAreEq
-> record style
-> (name ::: signal `Of` val) style
-> (remainder style,(lookupName ::: lookupSignal `Of` lookupVal) style)
instance (Dissection record remainder lookupName lookupSignal lookupVal) =>
NameMatchDep False
record name signal val
remainder lookupName lookupSignal lookupVal where
nameMatchDepExtract _ record _ = dissect record
instance (Record record, lookupSignal ~ signal, lookupVal ~ val) =>
NameMatchDep True
record lookupName signal val
record lookupName lookupSignal lookupVal where
nameMatchDepExtract _ record field = (record,field)
class Present lookupName
type SignalRecord era record = record (SignalStyle era)
data SignalStyle era
data instance (name ::: signal `Of` val) (SignalStyle era) = !name ::= signal era val
type ConsumerRecord record = ConnectorRecord Consumer record
type ProducerRecord record = ConnectorRecord Producer record
type ConnectorRecord connector record = record (ConnectorStyle connector)
data ConnectorStyle (connector :: (* -> * -> *) -> * -> *)
data instance (:::) name
(signal `Of` val)
(ConnectorStyle connector) = !name ::~ connector signal val
consume :: (Record record) => ConsumerRecord record -> Circuit era (SignalRecord era record) ()
consume = case build nilConsumeThing consConsumeThing of ConsumeThing result -> result
newtype ConsumeThing era record = ConsumeThing (ConsumerRecord record ->
Circuit era (SignalRecord era record) ())
nilConsumeThing :: ConsumeThing era X
nilConsumeThing = ConsumeThing $ \X -> arr (\X -> ())
consConsumeThing :: ConsumeThing era record
-> ConsumeThing era (record :& name ::: signal `Of` val)
consConsumeThing (ConsumeThing consume) = ConsumeThing consume' where
consume' (consumerRecord :& _ ::~ consumer) = proc (signalRecord :& _ ::= signal) -> do
consume consumerRecord -< signalRecord
Signal.consume consumer -< signal
produce :: (Record record) => ProducerRecord record -> Circuit era () (SignalRecord era record)
produce = case build nilProduceThing consProduceThing of ProduceThing result -> result
newtype ProduceThing era record = ProduceThing (ProducerRecord record ->
Circuit era () (SignalRecord era record))
nilProduceThing :: ProduceThing era X
nilProduceThing = ProduceThing $ \X -> arr (const X)
consProduceThing :: ProduceThing era record
-> ProduceThing era (record :& name ::: signal `Of` val)
consProduceThing (ProduceThing produce) = ProduceThing produce' where
produce' (producerRecord :& name ::~ producer) = proc () -> do
signalRecord <- produce producerRecord -< ()
signal <- Signal.produce producer -< ()
returnA -< signalRecord :& name ::= signal