【问题标题】:Abstracting Hspec tests抽象 Hspec 测试
【发布时间】:2016-12-09 02:50:39
【问题描述】:

我正在阅读“从第一原理开始的 Haskell 编程”,我发现自己一遍又一遍地以以下方式编写代码:

type IntToInt = Fun Int Int
type TypeIdentity = ConcreteFunctorType Int -> Bool
type TypeComposition = ConcreteFunctorType Int -> IntToInt -> IntToInt -> Bool

checkSomething :: IO ()
checkSomething = hspec $ do
        describe "Some functor" $ do
            it "identity property" $ do
                property $ (functorIdentity :: TypeIdentity)
            it "composition property" $ do
                property $ (functorComposition :: TypeComposition)

我尝试将其抽象化,但在我的水平上,我无法找到使其工作的方法

我本来希望完成的事情是这样的

checkFunctor :: (Functor f) => String -> f a -> IO ()
checkFunctor description f = hspec $ do
        describe description $ do
            it "identity property" $ do
                property $ (functorIdentity :: f a -> TypeIdentity)
            it "composition property" $ do
                property $ ( functorComposition :: f a -> TypeComposition)

编辑: 在 Sapanoia 的回答之后,我尝试如下

type TypeIdentity = Bool
type TypeComposition = Fun Int Int -> Fun Int Int -> Bool


checkFunctor :: forall f a. (Functor f) => String -> f a -> IO ()
checkFunctor description f = hspec $ do
    describe description $ do
        it "identity property" $ do
            property $ (functorIdentity :: f a -> TypeIdentity)
        it "composition property" $ do
            property $ (functorCompose' :: f a -> TypeComposition)

但我收到以下错误

FunctorCheck.hs:22:25: error:
• Couldn't match type ‘a’ with ‘Int’
  ‘a’ is a rigid type variable bound by
    the type signature for:
      checkFunctor :: forall (f :: * -> *) a.
                      Functor f =>
                      String -> f a -> IO ()
    at FunctorCheck.hs:16:26
  Expected type: f a -> TypeComposition
    Actual type: f Int -> Fun Int Int -> Fun Int Int -> Bool

然后定义类型以生成任意值和函数对我来说变得相当复杂。

有没有办法可以将 checkFunctor 的类型绑定到特定类型,如下所示?

checkFuntor :: checkFunctor :: forall f Int. (Functor f) => String -> f a -> IO ()

当然我试过这个,它给了我一个解析错误,我认为这只是我没有正确使用'forall'。

【问题讨论】:

  • 是的,有可能。您可以使用ScopedTypeVariables 来做到这一点,并且可以通过使用 GHC 8 的 TypeApplicationsAllowAmbiguousTypes 来做得更好。
  • forall 用于引入一个新的类型变量。不需要引入Int具体类型,只需插入它们而不是类型变量。然后您的示例变为:checkFunctor :: forall f. (Functor f) => String -> f Int -> IO ()。在checkFunctor 的正文中,您需要相应地:functorIdentity :: f Int -> TypeIdentity

标签: haskell quickcheck hspec


【解决方案1】:

由于您没有添加错误消息,我认为问题是定义了(functorIdentity :: f a -> TypeIdentity) 的类型错误。问题是这里介绍的f是新的,与顶层签名中的f不同。要解决此问题,请启用以下扩展:

{-# LANGUAGE ScopedTypeVariables #-}

并将checkFunctor的签名改为:

checkFunctor :: forall f a. (Functor f) => String -> f a -> IO ()

forall 引入了新的类型变量。如果没有 ScopedTypeVariables 和显式 forall,它总是隐式存在,(functorIdentity :: f a -> TypeIdentity) 变为 (functorIdentity :: forall f a. f a -> TypeIdentity)。但是,您确实想要一个 forall,因为您希望类型变量 fa 与顶级变量相同。

【讨论】:

  • 感谢您的回答!我试过这个后编辑了这个问题,虽然没有成功
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-16
相关资源
最近更新 更多