dev* 2015
examples in Haskell,
but the ideas carry to all functional languages
Disclaimer: I am yet to find the time to think about laws and prove my claims, so be a sceptical reader!
foo :: a -> b
"foo is a function from value of type a to value of type b"
public static <A,B> B foo(A a) {...} // java
(.) :: (b -> c) -> (a -> b) -> (a -> c)
baz = bar . foo
"dot is a function from two functions to a third function"
baz = bar ∘ foo // math
Function<A,C> baz = bar.compose(foo) // java
(types are commented out,
they carry no required information)
a regular function
from a value to a value of possibly another type
--functionAB :: [Int] -> Int
functionAB a = minimum a
--functionBC :: Int -> Int
functionBC b = 42 `div` b
--function :: [Int] -> Int
function = functionBC . functionAB
a lifted or a contextual function
like a -> b
but lifted to work with values in a context
public static <A,B> F<B> foo(F<A> a) {...} // java
this is easily achieved by making f a Functor
--liftedAB :: Maybe [Int] -> Maybe Int
liftedAB = fmap functionAB
--liftedBC :: Maybe Int -> Maybe Int
liftedBC = fmap functionBC
--lifted :: Maybe [Int] -> Maybe Int
lifted = liftedBC . liftedAB
sometimes we want to include some
pass-through context
logging, profiling...
still composable?
an applicative style function
like a -> b
but whole function wrapped in a context
allows the abstraction to do something with the function
public static /* exercise for the reader ;) */ {...} // java
-- composition for Applicatives (why is this not in hackage?)
(<.>) :: Applicative f => f (b -> c) -> f (a -> b) -> f (a -> c)
f <.> g = (.) <$> f <*> g
-- Tuple2 happens to be a suitable Applicative:
type Logged = (,) [String]
--applicativeAB :: Logged ([Int] -> Int)
applicativeAB = (["calculating minimums..."], minimum)
--applicativeBC :: Logged (Int -> Int)
applicativeBC = (["dividing by the value..."], (42 `div` ))
--applicative :: Logged ([Int] -> Int)
applicative = applicativeBC <.> applicativeAB
our example would sometimes divide by zero
and fails with an empty array
could we catch these in the type system?
a monadic function
public static <M,A,B> M<B> foo(A a) {...} // java, in theory...
--monadicAmB :: [Int] -> Maybe Int
monadicAmB [] = Nothing
monadicAmB a = Just (minimum a)
--monadicBmC :: Int -> Maybe Int
monadicBmC 0 = Nothing
monadicBmC b = Just (42 `div` b)
--monadic :: [Int] -> Maybe Int
monadic = monadicBmC <=< monadicAmB
our example has a magic constant
perhaps we would like to get the constant from
an environment?
a comonadic function
public static <W,A,B> B foo(W<A> wa) {...} // java, in theory...
-- Tuple2 happens to be a suitable Comonad,
-- so we can simply pass in the environment e as the left side:
type WithEnv e = (,) e
--comonadic_mAB :: WithEnv Int [Int] -> Int
comonadic_mAB (e,[]) = e
comonadic_mAB (e,a) = minimum a
--comonadic_mBC :: WithEnv Int Int -> Int
comonadic_mBC (e,0) = e
comonadic_mBC (e,b) = e `div` b
--comonadic :: WithEnv Int [Int] -> Int
comonadic = comonadic_mBC =<= comonadic_mAB
foo :: a -> b
bar :: b -> c
baz = bar . foo
foo :: f a -> f b
bar :: f b -> f c
baz = bar . foo
foo :: f (a -> b)
bar :: f (b -> c)
baz = bar <.> foo
<.>
is defined in a few random places...
foo :: a -> m b
bar :: b -> m c
baz = bar <=< foo
<=<
is defined in Control.Monad
foo :: w a -> b
bar :: w b -> c
baz = bar =<= foo
=<=
is defined in Control.Comonad
monad is just an abstraction for
composing certain kind of functions
when functions like a -> m b
compose
(and obey identity and associativity laws)
m
forms a monad
Similarly for comonad. That's it. No burritos.
with monadic functions
you can affect the subsequent behavior
via contextual return values
with comonadic functions,
their behavior can be affected
by context from the input
a passionate developer would like to have both!
--both_wAmB :: WithEnv Int [Int] -> Maybe Int
both_wAmB (e,[]) = Nothing
both_wAmB (e,a) = Just (minimum a)
--both_wBmC :: WithEnv Int Int -> Maybe Int
both_wBmC (e,0) = Nothing
both_wBmC (e,b) = Just (e `div` b)
-- Whoops, how to compose these?
--both :: WithEnv Int [Int] -> Maybe Int
both = undefined
a -> b f a -> f b a -> m b w a -> b f (a -> b) ?
an applicative f (a -> b)
is a really general absraction
can only apply the given argument and return the result
to interact with input and output we need more
data MyFunctionType a b = MyFunctionType (WithEnv Int a -> Maybe b)
could make this an Applicative...
...but wouldn't help with composition
class Category cat where
id :: cat a a
(.) :: cat b c -> cat a b -> cat a c
category gives us composition
instance Category MyFunctionType where
id = MyFunctionType $ Just . snd
MyFunctionType g . MyFunctionType f = MyFunctionType $
\(e,a) -> case f (e,a) of
Nothing -> Nothing
Just b -> g (e,b)
--cat_AB :: MyFunctionType [Int] Int
cat_AB = MyFunctionType f
where f (_,[]) = Nothing
f (_,a) = Just (minimum a)
--cat_BC :: MyFunctionType Int Int
cat_BC = MyFunctionType f
where f (_,0) = Nothing
f (e,b) = Just (e `div` b)
--cat :: MyFunctionType [Int] Int
cat = cat_BC . cat_AB
now we can compose our sophisticated type
so that's it
... or could we get more?
class Functor f where
fmap :: (a -> b) -> f a -> f b
actually, a covariant functor
class ContraFunctor f where
contramap :: (a -> b) -> f b -> f a
a contravariant functor
homework: Google for covariance and contravariance
class Profunctor p where
dimap :: (a -> b) -> (c -> d) -> p b c -> p a d
a functor of two type parameters,
first contravariant, second covariant
contravariant argument can be seen as input,
covariant as output
an ordinary function is a profunctor (it has input and output), but other things can be profunctors too
class Profunctor p => Strong p where
first' :: p a b -> p (a, c) (b, c)
capability to "drop in" values to "pass through"
simple and generic concept
combining these we get
component networks!
stream transformers, simple automata, FRP, Music signals, ...
class Profunctor p => Choice p where
left' :: p a b -> p (Either a c) (Either b c)
can "choose its output type based on its input"
this gives us branching!
class Profunctor p => Costrong p where
unfirst :: p (a, d) (b, d) -> p a b
can "drop out" values from input and output type
this gives us feedback loops!
all these simple concepts
are combined in a powerful interface:
instance Arrow MyFunctionType where
arr = MyFunctionType . dimap snd Just
first = first'
instance ArrowChoice MyFunctionType where
left = left'
instance ArrowLoop MyFunctionType where
loop = unfirst
different kinds of functions and "functions"
all can be composed,
although the operator looks different
but it's just syntax
functional programming is all about
functions and composition
we can do composition
even though our functions are a bit weird
we can do composition
even when our functions are more than functions
recognize when you need
function composition
in any language
Haskell abstracts everything
but the concepts carry to other languages
this was just the tip of the iceberg
keep learning!
code from this presentation:
https://gist.github.com/jyrimatti/797a9546fa0b456fc948