在Haskell中计算N-Ary(具有不同类型!!)笛卡尔积

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

And*_*ács 4

这里可以使用通常的异构列表:

{-# 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)