是否可以在Haskell中键入可变参数函数?

时间:2016-01-27 13:15:22

标签: haskell types dependent-type

请注意以下Haskell术语:

callNTimes :: forall a . Int -> (a -> a) -> a -> a
callNTimes n f 0 = x
callNTimes n f x = f (callNTimes (n-1) f x)

firstOf :: ??????
firstOf n = callNTimes n (\ x y -> x)

如果我们忽略类型并手动规范化函数,firstOf是一个接收N然后N参数的函数,丢弃除第一个之外的所有函数并返回它。 firstOf 3 10 20 30返回3。是否可以使用新的依赖类型功能在GHC 8.0中键入该功能?

2 个答案:

答案 0 :(得分:5)

我终于设法得到了一个正常工作的版本 - 它并不完全符合您的要求,但它展示了我评论的内容,我认为它非常接近

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE MultiParamTypeClasses #-}

module Variadic where

data Z a = Z
data S t a = S (t a)

class Var n where
  type El n :: *
  type Res n :: *
  firstOf :: n -> El n -> Res n

instance Var (Z a) where
  type El (Z a) = a
  type Res (Z a) = a
  firstOf Z a = a

instance (El (n a) ~ a, Var (n a)) => Var (S n a) where
  type El (S n a) = a
  type Res (S n a) = a -> Res (n a)
  firstOf (S n) a _ = firstOf n a

以下是一些例子:

λ> firstOf Z 5
5
λ> firstOf (S Z) 5 9
5
λ> firstOf (S (S Z)) 5 8 9
5
λ> firstOf (S (S Z)) "Hi" "World" "Uhu"
"Hi"

如果您对我如何到达那里感兴趣,可以查看编辑历史

备注

  • 它使用SZ作为穷人替换*用于类型级文字,您可以使用此
  • 您希望2个参数的版本firstOf (S (S Z))正在等待3 - 那是因为我从Z = 1参数开始
  • 我刚看到你想要firstOf 3 10 20 30 = 3这不会做(这会给10) - 这可能是类型级文字和明显的重载

答案 1 :(得分:2)

{-# LANGUAGE GADTs, DataKinds, TypeFamilies, TypeOperators #-}

type family Fun as b where
    Fun '[]       b = b
    Fun (a ': as) b = a -> Fun as b

data SL as where
    Sn :: SL '[]
    Sc :: SL as -> SL (a ': as)

constN :: SL as -> b -> Fun as b
constN  Sn    y = y
constN (Sc s) y = \_ -> constN s y

-- 1
main = print $ constN (Sc (Sc (Sc Sn))) 1 "a" [3] True

E.g。 Fun [Int, Bool] [Int] = Int -> Bool -> [Int]SL是一个允许将列表提升到类型级别的单例。 SL as上的模式匹配显示as[]a:as。在第一种情况下,目标的类型Fun [] b仅为b。在第二种情况下,目标具有类型a -> Fun as b,因此是lambda。

只有GADTs的简单解决方案:

{-# LANGUAGE GADTs #-}

data T z a where
    Tz :: T z z
    Ts :: T z b -> T z (a -> b)

constN :: T z a -> z -> a
constN  Tz    y = y
constN (Ts s) y = \x -> constN s y

-- 1
main = print $ constN (Ts (Ts (Ts Tz))) 1 "a" [3] True