2015-03-05 5 views
3

Текущая функция curry принимает функцию, принимающую кортеж из 2-х элементов и позволяющая результирующей функции быть нарисованной или частично примененной.Haskell, возможно ли создать функцию карри, которая может каррировать любое количество элементов кортежа.

let x = curry (\(x, y) -> x + y) 
x 1 2 -- 3 

Возможно ли создать функцию карри, которая может работать с функциями, которые имеют N элементов в своих кортежах?

Я попытался создать его, но я не уверен в сигнатуре типа 1: и 2: как изменить параметры.

curryN f 0 = f 
curryN f n = \a -> (curryN (f) (n-1)) a 

curryN (\(x, y, z) -> x + y + z) 3 
-- I assume it looks something like: \a -> (\a -> (\a -> (f) a) a) a but I'm not sure 

ИЛИ

curryN f 0 = f 
curryN f n = curryN (\a - > f a) (n -1) 

На стороне записки, может такая функция, то обнаружить количество элементов, а не необходимости быть сказано, что это число?

+7

Если 'n' является статически известным (например, с прокси-номером или типом уровня), это прекрасное упражнение для семейств типов. См. [Uncurry for n-arry functions] (http://stackoverflow.com/questions/24843224/uncurry-for-n-ary-functions). Использование вложенных пар вместо n-кортежей должно допускать рекурсивное решение. – chi

+0

Не в стандартном Haskell (например, Haskell 98 или Haskell 2010). –

+0

Связанные: https://ro-che.info/articles/2013-01-29-generic-uncurry –

ответ

3

Одним из способов реализации такой функции является использование GHC.Generics. При таком подходе нам даже не нужно передавать ряд параметров (или размер кортежа). Это работает, потому что существует экземпляр Generic, определенный для кортежей, который эффективно преобразует кортеж в древовидную структуру (типа Rep a), которую мы можем затем перемещать справа налево (используя здесь стиль продолжения прохождения), генерируя карриную функцию по пути и упаковке значения этих параметров в идентичную структуру Rep a, которая затем преобразуется в кортеж с помощью функции to и передается исходному непереносимому функциональному параметру. Этот код использует только дерево параметров уровня типа (функция from не используется), так как мы создаем кортеж, а затем получаем его. Единственным ограничением этого подхода является то, что Generic определяется только для 8-элементного кортежа.

{-# LANGUAGE TypeOperators, MultiParamTypeClasses, 
    FlexibleInstances, UndecidableInstances, 
    TypeFamilies, ScopedTypeVariables #-} 

import GHC.Generics 


-- | class for `curryN` function 
class CurryN t r where 
    type CurriedN t r :: * 
    curryN :: (t -> r) -> CurriedN t r 

-- | Implementation of curryN which uses GHC.Generics 
instance (Generic t, GCurryN (Rep t) r) => CurryN t r where 
    type CurriedN t r = GCurriedN (Rep t) r 
    curryN f = gcurryN (f . to) 

-- | Auxiliary class for generic implementation of `curryN` 
-- Generic representation of a tuple is a tree of its elements 
-- wrapped into tuple constructor representation 
-- We need to fold this tree constructing a curried function 
-- with parameters corresponding to every elements of the tuple 
class GCurryN f r where 
    type GCurriedN f r :: * 
    gcurryN :: (f p -> r) -> GCurriedN f r 

-- | This matches tuple definition 
-- Here we extract tree of tuple parameters and use other instances to "fold" it into function 
instance (GCurryN f r) => GCurryN (D1 e1 (C1 e2 f)) r where 
    type GCurriedN (D1 e1 (C1 e2 f)) r = GCurriedN f r 
    gcurryN c = gcurryN (\t -> c (M1 (M1 t))) 

-- | A node of the tree (combines at least two parameters of the tuple) 
instance (GCurryN b r, GCurryN a (GCurriedN b r)) => GCurryN (a :*: b) r where 
    type GCurriedN (a :*: b) r = GCurriedN a (GCurriedN b r) 
    gcurryN c = gcurryN (\a -> gcurryN (\b -> c (a :*: b))) 

-- | A leaf of the tree (a single tuple parameter) 
instance GCurryN (S1 NoSelector (Rec0 a)) r where 
    type GCurriedN (S1 NoSelector (Rec0 a)) r = a -> r 
    gcurryN c = \a -> c $ M1 (K1 a) 


-- Examples of usage 
t2 = curryN (uncurry (&&)) 

t3 = curryN (\(a,b,c) -> a + b + c) 

t4 = curryN (\(a,b,c,d) -> ((a , b) , (c , d))) 

tf = curryN (\(f,a,xs) -> foldr f a xs) 

t5 = curryN (\(a,b,c,d,e) -> (a ++ b , c - d, not e)) 

t7 = curryN (\(a1,a2,a3,a4,a5,a6,a7) -> a7)