Haskell, 是否可以创建一个可以柯里化任意数量的元组元素的 curry 函数
Haskell, is it possible to create a curry function that can curry any number of tuple elements
当前的 curry 函数采用接受 2 个元素的元组的函数,并允许对生成的函数进行柯里化或部分应用。
let x = curry (\(x, y) -> x + y)
x 1 2 -- 3
是否可以创建一个 curry 函数来处理元组中有 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)
附带说明一下,这样的函数可以发现元素的数量而不需要被告知数字是多少吗?
实现此类功能的方法之一是使用GHC.Generics。使用这种方法,我们甚至不需要传递大量参数(或元组大小)。
这是有效的,因为有一个为元组定义的 Generic
实例,它有效地将元组转换为树结构(Rep a
类型),然后我们可以从右到左遍历(在这里使用连续传递样式)生成沿途柯里化函数并将这些参数的值打包到相同的 Rep a
结构中,然后将其转换为具有 to
函数的元组并传递给原始的未柯里化函数参数。此代码仅使用类型级参数树(未使用 from
函数),因为我们生成元组而不是接收它。
这种方法的唯一限制是 Generic
仅针对最多八个元素的元组定义。
{-# 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)
当前的 curry 函数采用接受 2 个元素的元组的函数,并允许对生成的函数进行柯里化或部分应用。
let x = curry (\(x, y) -> x + y)
x 1 2 -- 3
是否可以创建一个 curry 函数来处理元组中有 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)
附带说明一下,这样的函数可以发现元素的数量而不需要被告知数字是多少吗?
实现此类功能的方法之一是使用GHC.Generics。使用这种方法,我们甚至不需要传递大量参数(或元组大小)。
这是有效的,因为有一个为元组定义的 Generic
实例,它有效地将元组转换为树结构(Rep a
类型),然后我们可以从右到左遍历(在这里使用连续传递样式)生成沿途柯里化函数并将这些参数的值打包到相同的 Rep a
结构中,然后将其转换为具有 to
函数的元组并传递给原始的未柯里化函数参数。此代码仅使用类型级参数树(未使用 from
函数),因为我们生成元组而不是接收它。
这种方法的唯一限制是 Generic
仅针对最多八个元素的元组定义。
{-# 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)