luo*_*990 6 haskell template-haskell
我知道该函数sequence可以处理[[1, 2], [3, 4]] -> [[1, 3], [1, 4], [2, 3], [2, 4]]问题.
但我认为真正的笛卡尔积应该处理这个([1, 2], ['a', 'b']) -> [(1, 'a'), (1, 'b'), (2, 'a'), (2, 'b')]问题,如果每个列表的类型不同,外部元组的类型(和大小)也应该关心.
所以,cartProd我想要的函数有这样的类型:([a1], [a2], [a3] ...) -> [(a1, a2, a3 ...)]
我知道类型系统存在一些问题.但有没有办法实现这个完美版本cartProd?
这里可以使用通常的异构列表:
{-# LANGUAGE
UndecidableInstances, GADTs,
TypeFamilies, MultiParamTypeClasses,
FunctionalDependencies, DataKinds, TypeOperators,
FlexibleInstances #-}
import Control.Applicative
data HList xs where
Nil :: HList '[]
(:>) :: x -> HList xs -> HList (x ': xs)
infixr 5 :>
-- A Show instance, for illustrative purposes here.
instance Show (HList '[]) where
show _ = "Nil"
instance (Show x, Show (HList xs)) => Show (HList (x ': xs)) where
show (x :> xs) = show x ++ " : " ++ show xs
Run Code Online (Sandbox Code Playgroud)
我们通常HLists使用类来编写函数,一个实例用于案例Nil,另一个实例用于案例:>。然而,仅仅为一个用例(即这里的笛卡尔积)创建一个类并不好,所以让我们将问题推广到应用排序:
class Applicative f => HSequence f (xs :: [*]) (ys :: [*]) | xs -> ys, ys f -> xs where
hsequence :: HList xs -> f (HList ys)
instance Applicative f => HSequence f '[] '[] where
hsequence = pure
instance (Applicative g, HSequence f xs ys, y ~ x, f ~ g) =>
HSequence g (f x ': xs) (y ': ys) where
hsequence (fx :> fxs) = (:>) <$> fx <*> hsequence fxs
Run Code Online (Sandbox Code Playgroud)
注意使用~请注意实例定义中约束它极大地帮助类型推断(以及类声明中的函数依赖);总体思路是将尽可能多的信息从实例头移至约束,因为这可以让 GHC 延迟解决它们,直到有足够的上下文信息。
现在笛卡尔积可以开箱即用:
> hsequence ([1, 2] :> "ab" :> Nil)
[1 : 'a' : Nil,1 : 'b' : Nil,2 : 'a' : Nil,2 : 'b' : Nil]
Run Code Online (Sandbox Code Playgroud)
我们还可以hsequence与任何一起使用Applicative:
> hsequence (Just "foo" :> Just () :> Just 10 :> Nil)
Just "foo" : () : 10 : Nil
Run Code Online (Sandbox Code Playgroud)
编辑:我发现(感谢 dfeuer)现有hlist包可以提供相同的功能:
import Data.HList.CommonMain
> hSequence ([3, 4] .*. "abc" .*. HNil)
[H[3, 'a'],H[3, 'b'],H[3, 'c'],H[4, 'a'],H[4, 'b'],H[4, 'c']]
Run Code Online (Sandbox Code Playgroud)