Как "распаковать" структуру в haskell

Недавно я столкнулся с этой проблемой и нашел решение, но мне интересно, есть ли лучшие (или просто более идиоматические) решения.

У меня есть структура для цвета:

data Rgb = Rgb Double Double Double

И есть функция, которую я хотел бы передать компонентам цвета отдельно, на самом деле из Каира:

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

Поэтому мне нужно как-то "распаковать" эту структуру данных, так как setSourceRGB не принимает Rgb. Я нашел два пути. Один из них - определить функцию для применения содержимого Rgb:

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

Тогда я могу сделать:

applyRgb setSourceRGB rgb

Другой способ, с которым я столкнулся, - сделать встроенное лямбда-выражение с флагом, что означает, что мне не нужно определять отдельную функцию:

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

Я не совсем доволен этим, но каким-то образом применение функции только для передачи некоторых значений кажется неправильным. Я бы хотел, чтобы он мог повернуть его и "преобразовать" Rgb в нужный тип для setSourceRGB. К сожалению, мне кажется, что невозможно иметь функцию

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

который можно передать в setSourceRGB. Возможно, applyRgb - лучшее решение, но мне интересно, есть ли лучший способ, который позволит мне выразить это как:

setSourceRGB (fromRgb rgb)

Ответ 1

Нет, вы не можете написать что-то вроде setSourceRGB (fromRgb rgb), потому что он просто даст один аргумент функции, поэтому applyRgb кажется лучшим решением. Если вам нравится такая штука, вы также можете использовать applyRgb в качестве функции infix:

setSource `applyRgb` rgb

Если вы часто используете эту функцию, ваш код может упростить чтение кода, указав имя для applyRgb setSource.

Ответ 2

Кстати, вы почти наверняка должны иметь:

data Rgb = Rgb !Double !Double !Double

и скомпилировать с -funbox-strict-полями, поэтому компоненты можно распаковать в примитивные двойные значения без распределения.

Ответ 3

Вы не можете "распаковать" что-либо в несколько аргументов, не обойдя сами функцию, как вы это выяснили.

Однако для согласованности я, вероятно, назвал бы помощником что-то вроде этого.

-- 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