haskell - Access environment in a function -
in main
can read config file, , supply runreader (somefunc) myenv
fine. somefunc
doesn't need access myenv
reader supplies, nor next couple in chain. function needs myenv tiny leaf function.
how access environment in function without tagging intervening functions (reader env)
? can't right because otherwise you'd pass myenv around in first place. , passing unused parameters through multiple levels of functions ugly (isn't it?).
there plenty of examples can find on net seem have 1 level between runreader , accessing environment.
i'm accepting chris taylor's because it's thorough , can see being useful others. heatsink 1 attempted directly answer question.
for test app in question i'll ditch reader altogether , pass environment around. doesn't buy me anything.
i must i'm still puzzled idea providing static data function h changes not type signature of g calls , f calls g. though actual types , computations involved unchanged. seems implementation details leaking on code no real benefit.
you do give return type of reader env a
, although isn't bad think. reason needs tag if f
depends on environment:
type env = int f :: int -> reader int int f x = env <- ask return (x + env)
and g
calls f
:
g x = y <- f x return (x + y)
then g
depends on environment - value bound in line y <- f x
can different, depending on environment passed in, appropriate type g
is
g :: int -> reader int int
this thing! type system forcing explicitly recognise places functions depend on global environment. can save typing pain defining shortcut phrase reader int
:
type global = reader int
so type annotations are:
f, g :: int -> global int
which little more readable.
the alternative explicitly pass environment around of functions:
f :: env -> int -> int f env x = x + env g :: env -> int -> int g x = x + (f env x)
this can work, , in fact syntax-wise it's not worse using reader
monad. difficulty comes when want extend semantics. depend on having updatable state of type int
counts function applications. have change functions to:
type counter = int f :: env -> counter -> int -> (int, counter) f env counter x = (x + env, counter + 1) g :: env -> counter -> int -> (int, counter) g env counter x = let (y, newcounter) = f env counter x in (x + y, newcounter + 1)
which decidedly less pleasant. on other hand, if taking monadic approach, redefine
type global = readert env (state counter)
the old definitions of f
, g
continue work without trouble. update them have application-counting semantics, change them to
f :: int -> global int f x = modify (+1) env <- ask return (x + env) g :: int -> global int g x = modify(+1) y <- f x return (x + y)
and work perfectly. compare 2 methods:
explicitly passing environment , state required complete rewrite when wanted add new functionality our program.
using monadic interface required change of 3 lines - , program continued work after had changed first line, meaning refactoring incrementally (and test after each change) reduces likelihood refactor introduces new bugs.
Comments
Post a Comment