haskell - Why encode function in data type definition? -
i find hard intuition encoding function in data type definition. done in definition of state
, io
types, e.g.
data state s = state s -> (a,s) type io = realworld -> (a, realworld) -- type synonym though, not new type
i see more trivial example understand value possibly build on have more complex examples. e.g. have data structure, make sense encode function in 1 of data constructor.
data tree = node int (tree) (tree) (? -> ?) | e
i not sure trying here, example of function can encode in such type? , why have encode in type, not use normal function, don't know, maybe passed argument when needed?
really, functions data else.
prelude> :i (->)
data (->) b -- defined in
`ghc.prim'
instance monad ((->) r) -- defined in
`ghc.base'
instance functor ((->) r) -- defined in
`ghc.base'
this comes out naturally , without conceptually surprising if consider functions from, say, int
. i'll give them strange name: (remember (->) b
means a->b
)
type array = (->) int
what? well, what's important operation on array?
prelude> :t (data.array.!)
(data.array.!) :: ghc.arr.ix => ghc.arr.array e -> -> e
prelude> :t (data.vector.!)
(data.vector.!) :: data.vector.vector -> int -> a
let's define our own array type:
(!) :: array -> int -> (!) = ($)
now can do
test :: array string test 0 = "bla" test 1 = "foo"
fnarray> test ! 0
"bla"
fnarray> test ! 1
"foo"
fnarray> test ! 2
"*** exception: :8:5-34: non-exhaustive patterns in function test
compare to
prelude data.vector> let test = fromlist ["bla", "foo"]
prelude data.vector> test ! 0
"bla"
prelude data.vector> test ! 1
"foo"
prelude data.vector> test ! 2
"*** exception: ./data/vector/generic.hs:244 ((!)): index out of bounds (2,2)
not different, right? it's haskell's enforcement of referential transparency guarantees return values of function can interpreted inhabitant values of container. 1 common way @ functor
instance: fmap transform f
applies transformation values "included" in f
(as result values). works composing transformation after target function:
instance functor (r ->) fmap transform f x = transform $ f x
(though you'd of course better write fmap = (.)
.)
now, what's bit more confusing (->)
type constructor has 1 more type argument: argument type. let's focus on defining
{-# language typeoperators #-} newtype (:<-) b = backfunc (b->a)
to feel it:
show' :: show => string :<- show' = backfunc show
i.e. it's function arrows written other way around.
is (:<-) int
sort of container, how (->) int
resembles array? not quite. can't define instance functor (a :<-)
. yet, mathematically speaking, (a :<-)
is functor, of different kind: contravariant functor.
instance contravariant (a :<-) contramap transform (backfunc f) = backfunc $ f . transform
"ordinary" functors otoh covariant functors. naming rather easy understand if compare directly:
fmap :: functor f => (a->b) -> f a->f b contramap :: contravariant f => (b->a) -> f a->f b
while contravariant functors aren't commonly used covariant ones, can use them in same way when reasoning data flow etc.. when using functions in data fields, it's covariant vs. contravariant should foremostly think about, not functions vs. values – because really, there nothing special functions compared "static values" in purely functional language.
about tree
type
i don't think data type made really useful, can stupid similar type may illustrate points made above:
data tree' = node int (bool -> tree) | e
that is, disconsidering performance, isomorphic usual
data tree = node int tree tree | e
why? well, bool -> tree
similar array tree
, except don't use int
s indexing bool
s. , there 2 evaluatable boolean values. arrays fixed size 2 called tuples. , bool->tree ≅ (tree, tree)
have node int (bool->tree) ≅ node int tree tree
.
admittedly isn't interesting. functions fixed domain isomorphism obvious. interesting cases polymorphic on function domain and/or codomain, leads abstract results such state monad. in cases, can remember nothing seperates functions other data types in haskell.
Comments
Post a Comment