一般'无类型'哈斯克尔的STack

时间:2014-11-22 09:33:47

标签: haskell types monads existential-type impredicativetypes

我需要为我正在处理的事情实现一般堆栈。该堆栈应该能够容纳不同类型的元素。例如,(1,'c',True,“Strings”)。要支持的功能是top,pop和push。

元组是最自然的想法。

push x s = (x,s)
pop s = snd s
top s = (fst s, s)

但我也需要支持空堆栈。这里,pop(和)没有在()上定义。 所以我尝试创建一个新类型。

data Stack = Empty | forall x. Cons (x, Stack)
push x s = Cons (x,s)
pop s = case s of
        Empty -> Left s
        Cons (x, y) -> Right y
top s = case s of
        Empty -> (Left (), s)
        Cons (x,y) -> (Right x, s)

在这里,top给了我一个错误:

Couldn't match expected type ‘b’ with actual type ‘x’
  because type variable ‘x’ would escape its scope
This (rigid, skolem) type variable is bound by
  a pattern with constructor
    Cons :: forall x. (x, Stack) -> Stack,
  in a case alternative
  at try.hs:11:9-18
Relevant bindings include
  x :: x (bound at try.hs:11:15)
  top :: Stack -> (Either () b, Stack) (bound at try.hs:9:1)
In the first argument of ‘Right’, namely ‘x’
In the expression: Right x

如果我解决这个问题:

data Stack x = Empty | forall y. Cons (x, Stack y)

我得到了与pop相同的错误。

我也尝试添加这个:

type AnyStack = forall x. Stack x

但又一次得到了类似的错误:

Couldn't match expected type ‘b’ with actual type ‘Stack y’
  because type variable ‘y’ would escape its scope
This (rigid, skolem) type variable is bound by
  a pattern with constructor
    Cons :: forall x y. (x, Stack y) -> Stack x,
  in a case alternative
  at try.hs:8:9-19
Relevant bindings include
  y :: Stack y (bound at try.hs:8:18)
  pop :: Stack t -> Either (Stack t) b (bound at try.hs:6:1)
In the first argument of ‘Right’, namely ‘y’
In the expression: Right y

有没有人可以帮我解决这种堆栈的正确类型签名或类型定义?或者可以指点一些与此相关的好参考?

非常感谢先进!

修改

如果我能够为此堆栈包含get函数,那将是完美的。给定一个整数i和一个堆栈s,get将返回s的第i个元素。我希望,一旦推出,弹出和顶部整理出来,我可能会自己做到这一点,但我仍然无法做到。关于这个人的任何想法?

2 个答案:

答案 0 :(得分:1)

你应该不能在空元组上定义pop,但如果我们使用类型类来表示堆栈类型的情况,那么其余的就足够了。

class Stack h where
  push :: a -> h x -> h (a, x)
  pop :: h (a, x) -> (h x, a)
  top :: h (a, x) -> (h (a, x), a)
  top hax = let (_, a) = pop hax in (hax, a)

newtype S x = S x

instance Stack S where
  push a (S x) = S (a, x)
  pop (S (a, x)) = (S x, a)

如果你将push / pop / top和S抽象地与

一起公开
sempty :: S ()
sempty = S ()

您可以确保没有人可以构建病态堆栈。如果你对GADT没问题,那么有更好的编码。

data S h where
  Nil :: S ()
  Cons :: a -> S x -> S (a, x)

您可以直接公开此GADT,因为它已经没有类型违规。

instance Stack S where
  push = Cons
  pop (Cons a x) = (x, a)

答案 1 :(得分:1)

你需要无类型吗?如果您愿意使用高级GHC功能,可以执行以下操作:

{-# LANGUAGE GADTs, DataKinds, KindSignatures, TypeOperators #-}
{-# LANGUAGE FlexibleInstances, FlexibleContexts #-}

module Stack (Stack(..), push, pop, top, empty) where

data Stack (h :: [*]) where
    Empty :: Stack '[]
    Push :: x -> Stack xs -> Stack (x ': xs)

instance Show (Stack '[]) where
    showsPrec d Empty = showParen (d > 11) $ showString "Empty"

instance (Show x, Show (Stack xs)) => Show (Stack (x ': xs)) where
    showsPrec d (Push x xs) = showParen (d > 10) $
        showString "Push " . showsPrec 11 x . showChar ' ' . showsPrec 11 xs

instance Eq (Stack '[]) where
    _ == _ = True

instance (Eq x, Eq (Stack xs)) => Eq (Stack (x ': xs)) where
    (Push x xs) == (Push y ys) = x == y && xs == ys

instance Ord (Stack '[]) where
    compare _ _ = EQ

instance (Ord x, Ord (Stack xs)) => Ord (Stack (x ': xs)) where
    compare (Push x xs) (Push y ys) = case compare x y of
    EQ -> compare xs ys
    LT -> LT
    GT -> GT


push :: x -> Stack xs -> Stack (x ': xs)
push = Push

pop :: Stack (x ': xs) -> Stack xs
pop (Push _ xs) = xs

top :: Stack (x ': xs) -> x
top (Push x _) = x

empty :: Stack '[]
empty = Empty

ghci中的一些用法如下所示:

[1 of 1] Compiling Stack            ( typelist.hs, interpreted )
Ok, modules loaded: Stack.
*Stack> :t push True . push (Just 'X') . push 5 . push "nil" $ empty
push True . push (Just 'X') . push 5 . push "nil" $ empty
  :: Num x => Stack '[Bool, Maybe Char, x, [Char]]
*Stack> push True . push (Just 'X') . push 5 . push "nil" $ empty
Push True (Push (Just 'X') (Push 5 (Push "nil" Empty)))
*Stack> pop . push True . push (Just 'X') . push 5 . push "nil" $ empty
Push (Just 'X') (Push 5 (Push "nil" Empty))
*Stack> pop empty

<interactive>:75:5:
    Couldn't match type ‘'[]’ with ‘x0 : xs’
    Expected type: Stack (x0 : xs)
      Actual type: Stack '[]
    Relevant bindings include
      it :: Stack xs (bound at <interactive>:75:1)
    In the first argument of ‘pop’, namely ‘empty’
    In the expression: pop empty

请注意,此表示具有很好的功能,即在空堆栈上调用poptop是编译时错误。但是,使用起来稍微困难一些,因为你总是需要证明你是用非空堆栈来调用它。它有利于防止错误,但有时需要更多的簿记来说服编译器你做对了。这种表现形式是一个不错的选择并不确定。这取决于用例。