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