2015-04-07 59 views
4

考虑可爱的小HoleyMonoid库,它可以让你建立输入参数可变类printf函数如下:如何从容器中构建一个类型化的可变参数函数?

{-# LANGUAGE NoMonomorphismRestriction #-} 

import Control.Category 
import Data.HoleyMonoid 
import Prelude hiding ((.), id) 

foo = 
    now "hello " 
    . later id 
    . now ", you are " 
    . later show 
    . now " years old" 

bar = run foo 

-- > bar "alice" 42 
-- "hello alice, you are 42 years old" 

-- > bar 42 "alice" 
-- type error 

有没有什么办法来检查容器(列表,AST等)和建造这样基于其内容的功能?

当作玩具例子,你能想象像下面这样:

import Data.Monoid 

adder = go where 
    go [] = now (Sum 0) 
    go (x:xs) 
    | x == 5 = now 100 . go xs 
    | otherwise = later id . go xs 

-- hypothetical usage 
-- 
-- > :t run adder [1, 3, 5] 
-- Num a => Sum a -> Sum a -> Sum a 
-- 
-- > getSum $ run adder [1, 3, 5] 0 1 
-- 101 

adder失败时检查,但你可以看到我拍摄的。问题似乎是难以在任何地方保持计算状态,例如now 100later id处于不同类型。

+3

那么,“加法器”应该是什么类型?显然你想要的类型取决于列表的长度。这将是[依赖类型](https://en.wikipedia.org/wiki/Dependent_type)。 Haskell不是([相当](http://stackoverflow.com/questions/12961651/why-not-be-dependently-typed/13241158#13241158))依赖类型的语言,所以这是不可能的。有什么可行的是根据你给出的参数来决定类型,并在运行时崩溃,当这与列表不符时......但这不完全是Haskell惯用的。 – leftaroundabout

+3

不适用于定期列表。使用GADT-y列表,您可能会获得类似的例如如http://stackoverflow.com/a/25422805/3234959 – chi

+0

@leftaroundabout它可能不是惯用的,但这基本上是'Text.Printf'的作用。除了运行时检查,这个方法还有一个问题,就是它不能处理像'show'这样的多态函数。 –

回答

2

我会忽略HoleyMonoid库。

我们需要自然数:

data Nat = Z | S Nat 

他们提升到型A级单:与

data Listy (b :: a -> *) :: [a] -> * where 
    Nilly :: Listy b '[] 
    Consy :: b x -> Listy b xs -> Listy b (x ': xs) 

然后:

data Natty :: Nat -> * where 
    Zy :: Natty Z 
    Sy :: Natty n -> Natty (S n) 

类型existentials的名单

type Natties = Listy Natty 

我们可以定义

adder :: Natties ns -> Adder ns 

其中ns :: [Nat]Adder类型系列定义如下:

type family Adder (ns :: [Nat]) :: * where 
    Adder '[]  = Int 
    Adder (n ': ns) = If (NatEq n (S (S (S (S (S Z)))))) Int (Int -> Adder ns) 

即,折叠列表Nat s,对列表中的每个数字加上(Int ->),直到遇到5(在Nat表单中)。它实际上应该是这样的

if_then_else_ b x y = if b then x else y 

type family Adder (ns :: [Nat]) :: * where 
    Adder '[]  = Int 
    Adder (n ': ns) = 'if_then_else_ (n '== 'fromInt 5) Int (Int -> Adder ns) 

但GHC引发一些令人毛骨悚然的错误,我不想了解。

NatEq型系列是明显的方式来定义:

type family NatEq n m :: Bool where 
    NatEq Z  Z = True 
    NatEq Z (S m) = False 
    NatEq (S n) Z = False 
    NatEq (S n) (S m) = NatEq n m 

我们需要在价值层面来比较Natty秒。两个Natty s为相等,当且仅当它们由相同数量的索引(这就是为什么Natty是一个单):

nattyEq :: Natty n -> Natty m -> Booly (NatEq n m) 
nattyEq Zy  Zy = Truly 
nattyEq Zy (Sy m) = Falsy 
nattyEq (Sy n) Zy = Falsy 
nattyEq (Sy n) (Sy m) = nattyEq n m 

哪里Booly是另一个单:

data Booly :: Bool -> * where 
    Truly :: Booly True 
    Falsy :: Booly False 

最后,adder定义:

adder = go 0 where 
    go :: Int -> Natties ns -> Adder ns 
    go i Nilly  = 0 
    go i (Consy n ns) = case nattyEq n (Sy (Sy (Sy (Sy (Sy Zy))))) of 
     Truly -> i + 100 
     Falsy -> \a -> go (i + a) ns 

iee求和所有参数,直到遇到5(以Natty的形式),并添加100。如果列表中没有5,则返回0

测试:

list = Consy Zy $ Consy (Sy Zy) $ Consy (Sy (Sy (Sy (Sy (Sy Zy))))) $ Consy Zy $ Nilly 

main = do 
    print $ adder (Consy Zy $ Consy (Sy Zy) $ Nilly) 3 9 -- 0 
    print $ adder list 6 8        -- 114 
    print $ adder (Consy (Sy (Sy Zy)) list) 1 2 3  -- 106 

code

+2

[Here](http://lpaste.net/130369)与'singletons'一样,FYI。 –

+0

@AndrásKovács,这很有启发性,谢谢。 – user3237465

相关问题