Re: [Haskell-cafe] [Haskell Cafe] Data construction: how to avoid boilerplate code?

On Wed, Jul 29, 2009 at 10:15 AM, Paul Sujkov
Hi Luke,
I'm not pretty sure. The thing I don't like is the need to copy-and-paste all the code with enumeration constructors. So, now I have two types to make Type data from, but they could be many, so I'll have many almost identical makeTypeFromFoo-functions. The thing I need is something like (*):
makeType :: ? -> Type makeType c = case c of ("-$-" or 240) -> Status ("-M-" or 64) -> Message ("-W-" or 32) -> Warning
Well, you could write a helper like this: matchType :: (Eq a) => (a,a,a) -> a -> Type matchType (status,message,warning) x | x == status = Status | x == message = Message | x == warning = Warning To reduce the size of your specifications: makeTypeStr = matchType ("-$-", "-M-", "-W-") makeTypeInt = matchType (240, 64, 32) There are trade-offs to doing something like this. It's smaller, but harder to read as specification. But, because it uses a tuple, it will catch you if you add a new case but forget to add it to one of the makeType*s (providing you remember to change matchType). What you're asking for puts all the conversions in the same place, which forbids them from being split out, decoupled, and modularlized. What if, instead of simple values, you had a more involved parser for these things? Even though it's kind of verbose, I think what you already have is fine. You do have to repeat the names, but it is still content code, and the relationship of the content to the names is explicit. You might find tables like this in a user's manual for your software... Luke
then I have all this fabric code in one place. I could use something like Either for this example, but it will scale fine up to two types to build value from; while I want to have arbitrary numbers of them
And from another point, it would be great if I could avoid doubling the code for datatype declaration with empty (no parameter) constructors, and actual "constructing" code from the different types. So it could look somewhat alike this (pseudo-code) (**):
data Type = Status <-- (String "-$-", Integer 240) | Message <-- (String "-M-", Integer 64) | Warning <-- (String "-W-", Integer 32)
however, I'm not sure this one is really needed. Something for the (*) is much more interesting
2009/7/29 Luke Palmer
On Wed, Jul 29, 2009 at 6:27 AM, Paul Sujkov
wrote: Hi haskellers,
I have a datatype of this sort:
data Type = Status | Message | Warning | Error | StepIn | StepOut deriving (Eq, Show)
and (at this moment) two fabric-like functions:
makeType :: String -> Type makeType c = case c of "-$-" -> Status "-M-" -> Message "-W-" -> Warning "-E-" -> Error "->>" -> StepIn "<<-" -> StepOut otherwise -> error "Uknown Message Type"
deduceType :: Integer -> Type deduceType n = case n of 240 -> Status 64 -> Message 32 -> Warning 8 -> StepOut 4 -> StepIn 2 -> Error otherwise -> error "Unknown Message Type"
how can I avoid boilerplate code at this stage? The thing that I really need is some n-type constructor, kind of a fabric for a variety of types with the possibility to add them on the fly (I have simple Integer and String here, but they could be much more sophisticated). I don't need the possibility to unpack the original value (e.g. "-$-" or 240) once the Type is created - in this case I can always have some sort of mapping to deduce it at any moment, so it will be redundant to carry it through the code explicitly
I am not sure what you're asking. Are you saying that what you have written is boilerplate? What code are you writing that could be automatically deduced with enough smartness?
Give an example of what you might like the solution to look like.
Luke
The example is rather short and simple, but I have some more places in code, where the same problem is observed. Is there any generic solution? Thanks in advance
-- Regards, Paul Sujkov
_______________________________________________ Haskell-Cafe mailing list Haskell-Cafe@haskell.org http://www.haskell.org/mailman/listinfo/haskell-cafe
-- Regards, Paul Sujkov

2009/7/29 Luke Palmer
On Wed, Jul 29, 2009 at 10:15 AM, Paul Sujkov
wrote: Hi Luke,
I'm not pretty sure. The thing I don't like is the need to copy-and-paste all the code with enumeration constructors. So, now I have two types to make Type data from, but they could be many, so I'll have many almost identical makeTypeFromFoo-functions. The thing I need is something like (*):
makeType :: ? -> Type makeType c = case c of ("-$-" or 240) -> Status ("-M-" or 64) -> Message ("-W-" or 32) -> Warning
Well, you could write a helper like this:
matchType :: (Eq a) => (a,a,a) -> a -> Type matchType (status,message,warning) x | x == status = Status | x == message = Message | x == warning = Warning
To reduce the size of your specifications:
makeTypeStr = matchType ("-$-", "-M-", "-W-") makeTypeInt = matchType (240, 64, 32)
There are trade-offs to doing something like this. It's smaller, but harder to read as specification. But, because it uses a tuple, it will catch you if you add a new case but forget to add it to one of the makeType*s (providing you remember to change matchType).
What you're asking for puts all the conversions in the same place, which forbids them from being split out, decoupled, and modularlized. What if, instead of simple values, you had a more involved parser for these things?
Even though it's kind of verbose, I think what you already have is fine. You do have to repeat the names, but it is still content code, and the relationship of the content to the names is explicit. You might find tables like this in a user's manual for your software...
Hi, Maybe two simple association lists would be acceptable ? Cheers, Thu
participants (2)
-
Luke Palmer
-
minh thu