Как "распаковать" структуру в 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