i'm trying match data constructors in generic way, task of type executed.
data task = tasktypea int | tasktypeb (float,float) generictasks :: statelikemonad s generictasks = want (tasktypea 5) tasktypea #> \input -> want (tasktypeb (1.2,4.3)) runtasktypea input tasktypeb #> \(x,y) -> runtasktypeb x y main = runtask generictasks in this, generictasks function goes through do-instructions, building list of stuff want handled sort of state monad, , list of ways it, via (#>) function. runtask function run generictasks, use resulting list of to-do , how-to-do, , computations.
however, i'm having quite trouble figuring out how extract "type" (tasktypea,b) (#>), such 1 can call later. if :t tasktypea, int -> task.
i.e., how write (#>)?
i'm not entirely confident it's possible i'm thinking here in such generic way. reference, i'm trying build similar shake library, (#>) similar (*>). shake uses string argument (*>), matching done entirely using string matching. i'd without requiring strings.
your intuition correct, it's not possible write (#>) have specified. time data constructor acts pattern when in pattern position, namely, appearing parameter function
f (tasktypea z) = ... as 1 of alternatives of case statement
case tt of tasktypea z -> ... or in monadic or pattern binding
do tasktypea z <- tt return z when used in value position (e.g. argument function), loses patterny nature , becomes regular function. means, unfortunately, cannot abstract on patterns easily.
there is, however, simple formalization of patterns:
type pattern d = d -> maybe it's little bit of work make them.
tasktypea :: pattern task int tasktypea (tasktypea z) = z tasktypea _ = nothing if need need use constructor "forwards" (i.e. a -> d), pair 2 (plus functions work it):
data constructor d = constructor (a -> d) (d -> maybe a) apply :: constructor d -> -> d apply (constructor f _) = f match :: constructor d -> d -> maybe match (constructor _ m) = m tasktypea :: constructor task int tasktypea = constructor tasktypea $ \case tasktypea z -> z _ -> nothing this known "prism", , (a general form of) implemented in lens.
there advantages using abstraction -- namely, can construct prisms may have more structure data types allowed (e.g. d can function type), , can write functions operate on constructors, composing simpler ones make more complex ones generically.
if using plain data types, though, pain have implement constructor objects each constructor did tasktypea above. if have lot of these work with, can use template haskell write boilerplate you. necessary template haskell routine already implemented in lens -- may worth learn how use lens library because of that. (but can bit daunting navigate)
(style note: second constructor above , 2 helper functions can written equivalently using little trick:
data constructor d = constructor { apply :: -> d, match :: d -> maybe } )
with abstraction in place, possible write (#>). simple example be
(#>) :: constructor d -> (a -> state d ()) -> state d () cons #> f = d <- case match cons d of nothing -> return () -> f or perhaps more sophisticated, depending on precisely want.
Comments
Post a Comment