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,027