Web Dev in Haskell with Fn PART 2
Credits for the following!
- Daniel Patterson (dbp.io)
- https://dbp.io/talks/2016/fn-continuations-haskell-meetup.pdf
Types!
site :: Ctxt -> IO Response
site ctxt =
route ctxt [ end ==> indexH
, path "hello" // param "name" ==> helloH
, path "add" // segment // segment // end ==> addNumbersH
, path "add" // segment // segment // end ==> addWordsH ]
`fallthrough` notFoundText "Page not found."
addNumbersH :: Ctxt -> Int -> Int -> IO (Maybe Response)
addNumbersH ctxt number1 number2 =
let sum = number1 + number2 in
okText (show number1 <> " plus " <>
show number2 <> " is " <> sum <> ".")
addWordsH :: Ctxt -> Text -> Text -> IO (Maybe Response)
addWordsH ctxt word1 word2 =
okText (word1 <> " plus " <> word2 <> " is " <> word1 <> word2 <> ".")
no arguments
one Text argument
two Int arguments
two Text arguments
this is a list of Routes that all seem very different -- how are they the same type?
Let's just look on Hackage and see what the types are
Let's start with
something simpler
printf
printf :: FormatString -> Arg -> ... -> Arg -> IO ()
(==>) :: Route -> (Arg -> ... -> Arg -> IO (Maybe Response))
"I have %d %s"
Int -> String
Int
"The answer is %d"
"Hello"
-
printf
examples = do
printf (c"hello")
printf (c"The answer is " % d) 42
printf (c"I have " % d % c" " % s) 2 "cats"
printf (c"hello") :: IO ()
printf (c"The answer is " % d) :: Int -> IO ()
printf (c"I have " % d % c" " % s) :: Int -> String -> IO ()
> Hello
> The answer is 42
> I have 2 cats
Tools in our toolbox
- Polymorphism
- Continuations
Polymorphism
id_int :: Int -> Int
id_int i = i
id_str :: String -> String
id_str s = s
id :: a -> a
id x = x
id (10 :: Int) -- a is Int
id ("Hello" :: String) -- a is String
id ((+ 1) :: Int -> Int) -- a is a function
id_f :: (Int -> Int) -> (Int -> Int)
id_f f = f
Continuations
printf :: ((String -> IO ()) -> a) -> a
printf format = format putStrLn
`printf` is a function that wants to know what to do next (a continuation)
a :: IO ()
a :: String -> IO ()
a :: String -> Int -> IO ()
`a` becomes the right type via polymorphism
Nothing needs formatted
One string
A string and an integer
How do we specify the format?
Constants
printf (c"hello") :: IO () -- prints "hello"
printf :: ((String -> IO ()) -> a) -> a
printf format = format putStrLn
c :: String -> (String -> a) -> a
c str = undefined
c :: String -> ((String -> IO ()) -> IO())
c str k = undefined
Too specific! Only allows IO actions
Good :D
printf :: ((String -> IO ()) -> IO ()) -> IO ()
printf format = format putStrLn
When format is c"hello", a is IO ()
Copy type of format to `c`
Constants, cont
c :: String -> (String -> a) -> a
c str k = undefined
c :: String -> (String -> a) -> a
c str k = k str
c :: String -> (String -> a) -> a
c str = undefined
let's list the arguments
well there's one thing we can do with these!
> printf (c "hello")
hello
> c "hello" id
"hello"
> c "hello" reverse
olleh
> c "hello" length
length
let's try this out:
Strings
printf s :: String -> IO ()
printf :: ((String -> IO ()) -> a) -> a
printf format = format putStrLn
s :: (String -> a) -> String -> a
s = undefined
s :: (String -> a) -> String -> a
s k str = k str
s = id
s :: (String -> IO ()) -> String -> IO ()
s = undefined
printf' :: ((String -> IO ()) -> String -> IO ()) -> String -> IO ()
printf' format = format putStrLn
:o
Integers
printf d :: Int -> IO ()
printf :: ((String -> IO ()) -> a) -> a
printf format = format putStrLn
d :: (String -> a) -> Int -> a
d k i = undefined
d :: (String -> a) -> Int -> a
d k i = k (show i)
printf :: ((String -> IO ()) -> Int -> IO ()) -> Int -> IO ()
printf format = format putStrLn
d :: (String -> IO ()) -> Int -> IO ()
d = undefined
Combining Strings, Integers, and Constants
(%) format1 format2 k = _
where concatK str1 str2 = k (str1 ++ str2)
printf :: ((String -> IO ()) -> a) -> a
printf format = format putStrLn
c :: String -> (String -> a) -> a
c str k = k str
s :: (String -> a) -> String -> a
s k str = k str
d :: (String -> a) -> Int -> a
d k i = k (show i)
printf (c"I have " % d % c" " % s) :: Int -> String -> IO ()
Combining Strings, Integers, and Constants
(%) format1 format2 k = _
where concatK str1 str2 = k (str1 ++ str2)
(%) format1 format2 k = format1 whatFormat1ShouldDo
where whatFormat1ShouldDo str = undefined
whatFormat2ShouldDo str = undefined
concatK str1 str2 = k (str1 ++ str2)
c :: String -> (String -> a) -> a
c str k = k str
s :: (String -> a) -> String -> a
s k str = k str
d :: (String -> a) -> Int -> a
d k i = k (show i)
Combining Strings, Integers, and Constants
(%) format1 format2 k = format1 whatFormat1ShouldDo
where
whatFormat1ShouldDo str1 = format2 (whatFormat2ShouldDo str1)
whatFormat2ShouldDo str1 str2 = undefined
concatK str1 str2 = k (str1 ++ str2)
(%) format1 format2 k = format1 whatFormat1ShouldDo
where whatFormat1ShouldDo str = undefined
whatFormat2ShouldDo str = undefined
concatK str1 str2 = k (str1 ++ str2)
(%) format1 format2 k = format1 whatFormat1ShouldDo
where
whatFormat1ShouldDo str1 = format2 (whatFormat2ShouldDo str1)
whatFormat2ShouldDo str1 str2 = concatK str1 str2
concatK str1 str2 = k (str1 ++ str2)
Combining Strings, Integers, and Constants
(%) format1 format2 k = format1 whatFormat1ShouldDo
where
whatFormat1ShouldDo str1 = format2 (concatK str1)
concatK str1 str2 = k (str1 ++ str2)
(%) format1 format2 k = format1 whatFormat1ShouldDo
where
whatFormat1ShouldDo = format2 . concatK
concatK str1 str2 = k (str1 ++ str2)
(%) format1 format2 k = format1 (format2 . concatK)
where
concatK str1 str2 = k (str1 ++ str2)
Try it out!!
printf (c"I love " % s) "computers"
printf (d % c" birds flew away") 3
printf (d % c"." % d % c"." % d % c"." % d) 127 0 0 1
Ideas:
- Can we make the type of `d` more general? How general can we make it?
- What other formats would be fun to make?
- Can we change printf to do other things besides `putStrLn`? What else would be fun?
Generalizing
If we want to make this work for any number, we can use the `Num` typeclass.
d :: (String -> a) -> Int -> a
d k i = k (show i)
d :: Num n => (String -> a) -> n -> a
d k i = k (show i)
Could not deduce (Show n) arising from a use of ‘show’
But that will give us this error:
Getting more specific
d :: Num n => (String -> a) -> n -> a
d k i = k (show i)
d :: (Num n, Show n) => (String -> a) -> n -> a
d k i = k (show i)
printf (c"I have " % d % c" " % s) 2.5 "cats"
-- > I have 2.5 cats
Getting THE MOST general!
d :: Show b => (String -> a) -> b -> a
d k anything = k (show anthing)
What if we wanted to make a formatter that worked for anything???
a :: (String -> a) -> b -> a
a k anything = undefined
printf (c"Favorite character: " % a) (Druid "Fero Feritas")
> Favorite character: Druid "Fero Feritas"
We can't. Some things, like functions, can't really be turned into Strings.
printf a printf
???
Getting more specific again
a :: Show b => (String -> a) -> b -> a
a k anything = k (show anthing)
Now we can format anything that is
an instance of the Show typeclass.
data Class = Bard String
| Fighter String
| Paladin String
| Druid String
deriving Show
printf (c"My character: " % a) (Fighter "Hela Veral")
> My character: Fighter "Hela Veral"
So this is probably the most general type we want to write.
Looks... familiar??
(%) :: ((String -> a) -> b)
-> ((String -> c) -> a)
-> (String -> c) -> b
(//) :: (Req -> IO (Maybe (Req, k -> k')))
-> (Req -> IO (Maybe (Req, k' -> a)))
-> Req -> IO (Maybe (Req, k -> a))
(==>) :: RequestContext ctxt
=> (Req -> IO (Maybe (Req, k -> a)))
-> (ctxt -> k)
-> ctxt -> Req -> IO (Maybe a)
printf :: ((String -> IO ()) -> a) -> a
printf format = format putStrLn
More details
- https://dbp.io/talks/2016/fn-continuations-haskell-meetup.pdf
www.github.com/emhoracek/fntutorial
fnhaskell.com/tutorial
fnhaskell.com
Web Dev in Haskell with Fn: Part 2
By emhoracek
Web Dev in Haskell with Fn: Part 2
- 1,120