tags:

views:

163

answers:

3

I recently came across this problem and found a solution but I'm wondering if there are better (or just more idiomatic) solutions.

I have a structure for a colour:

data Rgb = Rgb Double Double Double

And there is a function I'd like to pass the colour components to individually, actually from Cairo:

setSourceRGB :: Double -> Double -> Double -> Render ()

So I need to "unpack" this data structure somehow, since setSourceRGB doesn't take an Rgb. I found two ways. One is to define a function to apply the contents of an Rgb:

applyRgb :: (Double -> Double -> Double -> t) -> Rgb -> t
applyRgb f (Rgb r g b) = f r g b

Then I can do:

applyRgb setSourceRGB rgb

Another way I came up with is to do an inline lambda expression with a case, which means I don't have to define a separate function:

(\z -> (case z of (Rgb r g b) -> setSourceRGB r g b)) rgb

I'm not completely happy with this however, somehow applying a function just to pass some values doesn't seem right. I'd like to be able to turn it around, and "convert" the Rgb to the right type for setSourceRGB. Unfortunately it seems to me that's it's impossible to have a function

fromRgb :: Rgb -> Double -> Double -> Double

that can be passed to setSourceRGB. Perhaps applyRgb is the best solution, but I'm wondering if there's some better way that will let me express it as:

setSourceRGB (fromRgb rgb)
+3  A: 

No, you can't write something like setSourceRGB (fromRgb rgb), because it will just give one argument to the function, so applyRgb seems like the best solution. If you like this sort of thing, you can also use applyRgb as an infix function :

setSource `applyRgb` rgb

If you often use this function, your can make your code easier to read by defining a name for applyRgb setSource.

gnomnain
+2  A: 

BTW, you should almost certainly have:

data Rgb = Rgb !Double !Double !Double

instead, and compile with -funbox-strict-fields, so the components can be unpacked into allocation-free primitive double values.

Don Stewart
+2  A: 

You can't "unpack" anything into multiple arguments without wrapping the function itself the ways you've figured out.

However, for consistency, I'd probably name the helper something like this.

-- from Prelude...
uncurry :: (a -> b -> c) -> (a, b) -> c
uncurry f (a, b) = f a b

-- yours?
uncurryRgb :: (Double -> Double -> Double -> a) -> Rgb -> a
uncurryRgb f (Rgb r g b) = f r g b
ephemient
You should fix the definition to match the signature. Right now the cases differ.
Martijn
Augh, typo. Well, spelling differences aside, this is the same definition as OP's `applyRgb`...
ephemient