2015-04-20 2 views
3

Является ли это стандартным способом преобразования оператора по типу t оператору по типу a->t? То есть, является ли это Lib реализации этой функции (fT как functionTransformer):Оператор преобразования типа `t` оператору типа` a-> t`

fT :: (t -> t -> t) -> (a -> t) -> (a -> t) -> (a -> t) 
fT op f1 f2 = \x -> (f1 x) `op` (f2 x) 

(мы могли бы обобщать к fT :: (t1 -> t2 -> t) -> (t3 -> t1) -> (t3 -> t2) -> t3 -> t)

Я задавал этот вопрос, когда обучение Йесод: в этих рамках, мы можем добавить условие валидации для поля благодаря checkBool. Например, можно создать поле, которое принимает только значение более Тана 100 с:

smallIntField = checkBool (<= 100) "error: this entry has to be smaller than 100" intField 

Благодаря моей «функции трансформатора», можно легко управлять ограниченное значение с:

($&&) = fT (&&) 
boundedIntField = checkBool ((>= 0) $&& (<= 100)) "error: this entry has to be between 0 and 100" intField 
+2

это только меня или ваша * подпись * выходной - либо 'ор: : t -> t -> t' или вы даже «fT :: (t1 -> t2 -> t) -> (t3 -> t1) -> (t3 -> t2) -> t3 -> t' (говорит ghci);) – Carsten

+0

Действительно ли 'on' из' Data.Function' что вы ищете? – Mokosha

+0

@ CarstenKönig действительно ... Я редактировал свой пост. @Mokosha не совсем, с 'on', есть только одна' f', в моем случае есть 'f1' и' f2'. – Sebastien

ответ

6

Что вы» действительно ищут комбататоры liftA2 или liftM2 (от Control.Applicative или Control.Monad соответственно). liftM2 будет работать так же, как liftA2, поскольку все монады являются аппликативными, но это зависит от вас, если вы хотите быть более или менее ограничительным. Использование монадической реализации также заставит порядок оценки аргументов, которые не могут быть гарантированы liftA2.

Вы можете использовать его как

(<&&>) :: Applicative f => f Bool -> f Bool -> f Bool 
(<&&>) = liftA2 (&&) 

Для функций, таких как (>= 0) или (<= 100), The Applicative f => f получает специализируется на (Ord a, Num a) => (->) a, поэтому тип подписи будет

(<&&>) :: (Ord a, Num a) => (a -> Bool) -> (a -> Bool) -> (a -> Bool) 

Таким образом, вы могли бы написать что-то вроде

between :: Int -> Int -> Int -> Bool 
between lower upper = (lower <=) <&&> (<= upper) 

Если вы определили <||> = liftM2 (||), то вы можете даже сделать что-то более сложное, как

between 0 100 <||> between 200 400 <||> (>= 1000) :: Int -> Bool 

Что бы проверить, если число является элементом [0, 100] U [200, 400] U [1000, inf), если бы мы писали об этом множестве обозначений (U устанавливается союз).

2

Этот код находится в Control.Applicative модуле:

instance Applicative ((->) r) where 
    pure a = \_ -> a 
    ff <*> fa = \r -> ff r (fa r) 

liftA2 :: Applicative f => f (a -> b -> c) -> f a -> f b -> f c 
liftA2 f fa fb = pure f <*> fa <*> fb 

Ваша fT функция только то, что мы получаем, когда мы берем liftA2 и использовать (->) a для f и t для a, b и c. Мы можем переписать функцию так же, как это:

fT :: (t -> t -> t) -> (a -> t) -> (a -> t) -> (a -> t) 
fT = liftA2 

Подставляя, мы можем доказать, что это то же самое, как ваша:

liftA2 op f1 f2 
    = (pure op <*> f1) <*> f2 
    = \a -> (pure op <*> f1) a (f2 a) 
    = \a -> (\a -> pure op a (fa 1)) a (f2 a) 
    = \a -> (\a -> (\_ -> op) a (f1 a)) a (f2 a) 
    = \a -> (\a -> op (f1 a)) a (f2 a) 
    = \a -> op (f1 a) (f2 a) 
    = \a -> (f1 a) `op` (f2 a)