Как «распаковать» структуру в 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)
Решение
Нет, вы не можете написать что-то вроде setSourceRGB (fromRgb rgb), потому что это даст функции только один аргумент, поэтому applyRgb кажется лучшим решением.Если вам нравятся подобные вещи, вы также можете использовать applyRgb в качестве инфиксной функции:
setSource `applyRgb` rgb
Если вы часто используете эту функцию, вы можете облегчить чтение кода, определив имя для applyRgb setSource.
Другие советы
Кстати, у вас почти наверняка должно быть:
data Rgb = Rgb !Double !Double !Double
вместо этого и скомпилируйте с -funbox-strict-fields, чтобы компоненты можно было распаковать в примитивные двойные значения без выделения.
Вы не можете «распаковать» что-либо в несколько аргументов, не обернув саму функцию способами, которые вы выяснили.
Однако для последовательности я бы, наверное, назвал помощника примерно так.
-- 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