shark.fish/talk
shark.fish/try
pow :: Int -> Int -> Int
Define user interface and program logic:
pow <$> int "Base" 2
<*> int "Exponent" 8
type Signal a = Time -> a -- Conceptually
mousePos :: Signal (Int, Int)
seconds :: Signal Int
keyPressed :: Key -> Signal Bool
drawClock :: Int -> Drawing
drawClock secs = ..
animatedClock :: Signal Drawing
animatedClock = drawClock <$> seconds -- <$> is infix for `map`
progressBar :: Number -> Drawing
progressBar percentage = ..
ratio :: Signal Number
ratio = (/) <$> mousePosX <*> windowWidth
progress = progressBar <$> ratio
How can we build up the full user interface from an expression like:
pow <$> int "Base" 2
<*> int "Exponent" 8
→ Go from Signals to Flares
Annotate a base functor 'f
' with values of type 'a
':
data Ann a f b = Ann a (f b)
instance Functor f => Functor (Ann a f) where
map g (Ann a fb) = Ann a (map g fb)
Is Ann
also an applicative functor?
data Ann a f b = Ann a (f b)
instance Applicative f => Applicative (Ann a f) where
pure x = Ann ??? (pure x)
(Ann a1 f1) <*> (Ann a2 f2) = Ann ??? (f1 <*> f2)
… if we require 'a
' to be a Monoid
:
data Ann a f b = Ann a (f b)
instance (Monoid a, Applicative f) => Applicative (Ann a f) where
pure x = Ann mempty (pure x)
(Ann a1 f1) <*> (Ann a2 f2) = Ann (a1 <> a2) (f1 <*> f2)
Example: composition law
pure (.) <*> u <*> v <*> w = u <*> (v <*> w)
Proof:
pure (.) <*> u <*> v <*> w
= Ann mempty (pure (.)) <*> Ann mu fu <*> Ann mv fv <*> Ann mw fw
= Ann mu (pure (.) <*> fu) <*> Ann mv fv <*> Ann mw fw
= Ann (mu <> mv) (pure (.) <*> fu <*> fv) <*> Ann mw fw
= Ann ((mu <> mv) <> mw) (pure (.) <*> fu <*> fv <*> fw)
= Ann (mu <> (mv <> mw)) (fu <*> (fv <*> fw))
= Ann mu fu <*> Ann (mv <> mw) (fv <*> fw)
= Ann mu fu <*> (Ann mv fv <*> Ann mw fw)
= u <*> (v <*> w)
Note: this needs the identity and the associativity of the Monoid.
type Flare a = Ann [HTMLElement] Signal a
-- ^ ^
-- | |
-- Monoid to collect Output
-- UI elements type
type Label = String
string :: Label -> String -> Flare String
int :: Label -> Int -> Flare Int
boolean :: Label -> Boolean -> Flare Boolean
sayHello <$> string "Name" "LambdaConf"
<*> int "Year" 2016
<*> boolean "Shout" false
plot <$> (numberSlider "m" 2.0 10.0 1.0 5.0)
<*> (numberSlider "n" 3.0 10.0 0.1 4.0)
<*> (numberSlider "s" 4.0 16.0 0.1 14.0)
<*> (color "Fill color" indigo)
<*> (boolean "Animated" false)
<*> lift animationFrame
pow :: Int -> Int -> Int
Go from this:
flare =
pow <$> int "Base" 2
<*> int "Exponent" 8
runFlare flare
to this:
flareCheck pow
… and let the type checker do all the work.
class Arbitrary a where
arbitrary :: Gen a
class Testable t where
test :: t -> Gen Result
instance (Arbitrary a, Testable t) => Testable (a -> t)
class Flammable a where
spark :: Flare a
class Interactive t where
interactive :: Flare t -> Flare Renderable
instance (Flammable a, Interactive t) => Interactive (a -> t)
Flammable Boolean
Flammable Char
Flammable String
Flammable Int
Flammable Number
(Read a) => Flammable (List a)
(Read a) => Flammable (Array a)
(Flammable a) => Flammable (Maybe a)
(Flammable a, Flammable b) => Flammable (Tuple a b)
(Flammable a, Flammable b) => Flammable (Either a b)
Interactive Boolean
Interactive Char
Interactive String
Interactive Int
Interactive Number
(Flammable a, Interactive t) => Interactive (a -> t)
(Generic a) => Interactive (List a)
(Generic a) => Interactive (Array a)
(Generic a) => Interactive (Maybe a)
(Generic a, Generic b) => Interactive (Tuple a b)
(Generic a, Generic b) => Interactive (Either a b)
length :: String -> Int
flareCheck length
charCodeAt :: Int -> String -> Maybe Int
flareCheck charCodeAt
filter :: forall a. (a -> Boolean) -> Array a -> Array a
even :: Int -> Boolean
filter even :: Array Int -> Array Int
flareCheck (filter even)
xor :: Boolean -> Boolean -> Boolean
flareCheck xor
Ann
- Ann
cannot be made a (law-abiding) Monad
- Ann
as product of functors:
type Ann a f b = Product (Const a) f b
instance Monoid a => Applicative (Const a)
instance (Applicative f, Applicative g) => Applicative (Product f g)
(thanks to /r/purescript
!)