I have a bunch of functions like:
f1 :: String -> String -> ... -> String -> ()
f1 a b ... z = g [("a", a), ("b", b), ... ("z", z)]
...
fn :: String -> Int -> String -> ... -> String -> ()
fn a b ... z = g [("a", a), ("b", show b), ... ("z", z)]
So user can just call them like f1 "abc" "def". I don’t want him to do this because he can easily swap “abc” and “def” by mistake (and God knows how much time would be wasted while debugging). I want him to pass arguments like fk (A "abc") (B "def")
As far as I can see, there are 2 options:
-
Massive
dataconstruction and massive unpack function:data Value = A String | B String | C Int | D String ... unpack :: Value -> String unpack (A a) = a unpack (B b) = b unpack (C c) = show c unpack (D c) = dLots of code.
-
Common typeclass and newtypes:
EDIT: Okay then, we can useGeneralizedNewtypeDerivingin such simple case.{-# LANGUAGE GeneralizedNewtypeDeriving #-} class Value a where unpack :: a -> String instance Value String where unpack = id instance Value Int where unpack = show newtype A = A String deriving Value newtype B = B String deriving Value newtype C = C Int deriving Value newtype D = D String deriving Value ...Looks much better but all
fkwould look likefk a b ... z = g [("a", unpack a), ("b", unpack b), ... ("z", unpack z)]Lots of code and duplication.
What I want is some magic trick which would allow me:
fk a b ... z = g [("a", a), ("b", b), ... ("z", z)]g = h . map (second unpack)
I think the problems boils down to this: The list can have elements of the same type only; which means that either you have to ‘coalesce’ it into a single type in your
f, or you cannot rely on haskells type checks. E.g. the following code would work for you, but the type check is runtime:When you want compile-time type check, you can do something like this; however, you still have to retype the parameters to the same type, so in some sense, there is not much difference between unpacking it, only it might by slightly less error-prone, though. A nice trick comes from the json packages – they redefine some operator (e.g. =:) to create the type, so you would have:
It’s not that much different from just defining
a =: b = (a, unpack b)though.