design patterns - how to refactor this Haskell chain of functions code? -


i have software design experience, , learning haskell now. in many real world software developments, 1 faces situation 1 given, instance, below:

suppose, have code

f1 b c d = e     e1 = f2 b c (f3 a)   e2 = f4 d   e = e1 + e2  f2 b c d = n + c + d    n = f5 b  f5 n = n*n  f3 = * 2  f4 = + 3 

now if want change f5 takes yet parameter, have change chain of functions right upto f1. can done shown below. note added parameter x.

f1 b c d x = e -- f1 needs changed    e1 = f2 b c (f3 a) x   e2 = f4 d   e = e1 + e2  f2 b c d x = n + c + d -- f2 needs changed    n = f5 b x  f5 n x = n*n -- f5 changed (**bang**)  f3 = * 2  f4 = + 3 

is normal haskell way of doing type of thing or there better (more haskell-ish) way? know such change in api disturb client code, how impact can kept minimum , there hasekll way it?

on more general level: how haskell perform in such cases (especially taking consideration immutable state feature)? has offer developers in regard? or haskell has got no role, per se, play in , difficult software engineering problem (no such thing future proof) have keep with?

i apologize asking more 1 question in single post, cannot these related each other. also, not find similar question asked, sorry if might have missed it.

one thing can bundle parameters single parameter object, bhelkir suggests in comment. if add new parameter object still need change client code calls f1, , change direct consumers of new parameter (here, f5), both unavoidable: has provide x @ point, , need client; , have consume x somehow otherwise why adding begin with?

but, can avoid changing intermediary functions f1 , f2, because can ignore new fields don't care about. , can little fancy using applicative instance ((->) t) (generally called reader) pass along object, rather doing manually. here 1 way write that:

module test import control.applicative  data settings = settings {geta :: int,                           getb :: int,                           getc :: int,                           getd :: int} f1 :: settings -> int f1 = lifta2 (+) f2 f4 -- f1 = --   e1 <- f2 --   e2 <- f4 --   return $ e1 + e2  f2 :: settings -> int -- clever lifta3 , (+) possible here f2 s = f5 s + getc s + f3 s  f3 :: settings -> int f3 = lifta (* 2) geta  f4 :: settings -> int f4 = lifta (+ 3) getd  f5 :: settings -> int -- f5 = lifta (join (*)) getb -- perhaps bit opaque f5 = lifta square getb   square b = b * b 

now, has pluses , minuses: logic in f1 (ie, knowing need call f3 a) has moved f3 itself, , happen function read parameter , mucked before passing along subsidiary function. may clearer original, or may obscure intent behind f1, depending on problem domain. can write function more explicitly, eg having modify passed-in settings object change a field before passing along, did f2 example. more generally, can write function in whichever style convenient it: do-notation, applicative functions, or plain old pattern-matching on record object being passed in.

but big plus it's easy add new parameter: add field settings record, , read in function needs it:

module test import control.applicative  data settings = settings {geta :: int,                           getb :: int,                           getc :: int,                           getd :: int,                           getx :: int} f1 :: settings -> int f1 = lifta2 (+) f2 f4 -- f1 = --   e1 <- f2 --   e2 <- f4 --   return $ e1 + e2  f2 :: settings -> int -- clever lifta3 , (+) possible here f2 s = f5 s + getc s + f3 s  f3 :: settings -> int f3 = lifta (* 2) geta  f4 :: settings -> int f4 = lifta (+ 3) getd  f5 :: settings -> int f5 = lifta2 squareadd getb getx   squareadd b x = b * b + x 

note same except data settings , f5.


Comments

Popular posts from this blog

html - Firefox flex bug applied to buttons? -

html - Missing border-right in select on Firefox -

c# - two queries in same method -