gpt4 book ai didi

haskell - 如何指定带有约束的类型?

转载 作者:行者123 更新时间:2023-12-02 21:08:44 26 4
gpt4 key购买 nike

我想在一个简单的演示程序中定义一堆测试,其中每个测试都是本地定义的,但所有测试都可以在一个标准位置打印。

例如;

t1 = ("Sqrt(4)", sqrt(4.0))
...
t2 = ("sumList:", sum [1,2,3,4])
...
t3 = ("Description", value)
...

因此,每个测试的类型为:(字符串,值),对于各种值类型,所有这些值类型(仅)必须是 Show 类的成员。

然后是测试摘要,一个循环:

test (msg, val) = do print $ msg ++ " :: " ++ show val
tests ts = mapM test ts

这会编译并分配这些类型:

test :: Show a => ([Char], a) -> IO ()
tests :: (Traversable t, Show a) => t ([Char], a) -> IO (t ())

只有当所有测试的第二个参数都具有相同类型时,该方法才有效。我假设它以某种方式将类型专门化为实际遇到的参数类型,即使它们都是可显示的。

为了使它们可以根据第二个参数的实际类型而变化,我尝试了这样的操作(伪代码):

type ATest = (Show a) => (String, a)

由于这行不通,我尝试了:

{-# LANGUAGE RankNTypes #-}
type ATest = forall a. (Show a) => (String, a)

它可以编译,但在值参数的任何变化上仍然失败。

此外,我想从打印测试的循环中抽象出测试的类型,但我无法使用它来转换:

   test :: Show a => ([Char], a) -> IO ()
to
test :: ATest -> IO ()

基本思想是在测试循环的定义中定义并使用多态类型进行测试。所以也许是一个数据结构;

data (Show a) => ATest =  Test (String,a)

但这也失败了,尽管它确实给出了正确的想法;所有测试都有一个共同的结构,并在 Show 类型类中具有第二个值。

正确的方法是什么?

最佳答案

让我们首先评论一下 testtests 的推断类型:

test :: Show a => ([Char], a) -> IO ()
tests :: (Traversable t, Show a) => t ([Char], a) -> IO (t ())

Which works only as long as all tests have the same type for the second argument. I assume that it is somehow specializing the type to the actual encountered type of the arguments, even though they are all show'able.

这是预料之中的。列表(或任何其他可遍历的)的所有元素必须具有相同的类型。这甚至不完全是“特化”的问题:一旦类型检查器发现你试图例如用 IntString 组装一个列表,它立即举起手来:

GHCi> [3 :: Int, "foo"]

<interactive>:125:12: error:
• Couldn't match expected type ‘Int’ with actual type ‘[Char]’
• In the expression: "foo"
In the expression: [3 :: Int, "foo"]
In an equation for ‘it’: it = [3 :: Int, "foo"]

一种直接的规避方法是为元素分配一个类型,该类型忽略值之间不相关的差异。这正是你试图通过引入 forall 来实现的——在你的例子中,你试图声明对的第二个元素唯一重要的是有一个显示它们的实例:

{-# LANGUAGE RankNTypes #-}
type ATest = forall a. (Show a) => (String, a)

您提到这种方法“在值参数的任何变化上仍然失败”。我无法重现这种特定的失败模式:事实上,我什至无法说明 ATest 列表的类型:

GHCi> :set -XRankNTypes
GHCi> type ATest = forall a. (Show a) => (String, a)
GHCi> -- There is no special meaning to :{ and :}
GHCi> -- They are merely a GHCi trick for multi-line input.
GHCi> :{
GHCi| glub :: [ATest]
GHCi| glub = [("Sqrt(4)", sqrt(4.0)),("sumList:", sum [1,2,3,4])]
GHCi| :}

<interactive>:145:9: error:
• Illegal polymorphic type: ATest
GHC doesn't yet support impredicative polymorphism
• In the type signature:
glub :: [ATest]

GHC 即将拒绝:由于 ATest 只是一个类型同义词,[ATest] 扩展为 [forall a. (显示a)=>(字符串,a)]。列表类型构造函数的参数中的 forall 需要一个称为谓语多态性的功能,该功能is not supported by GHC 。为了避免遇到这种情况,我们需要定义一个正确的数据类型,而不仅仅是一个同义词,这就是您在最后一次尝试中尝试做的事情 - 只不过您仍然需要像以前一样的 forall :

GHCi> -- We need a different extension in this case.
GHCi> :set -XExistentialQuantification
GHCi> data ATest = forall a. Show a => ATest (String, a)
GHCi> :{
GHCi| glub :: [ATest]
GHCi| glub = [ATest ("Sqrt(4)", sqrt(4.0)),ATest ("sumList:", sum [1,2,3,4])]
GHCi| :}
GHCi>

这终于按预期工作了:

GHCi> :{
GHCi| -- I took the liberty of doing a few stylistic changes.
GHCi| test :: ATest -> IO ()
GHCi| test (ATest (msg, val)) = print $ msg ++ " :: " ++ show val
GHCi|
GHCi| tests :: Foldable t => t ATest -> IO ()
GHCi| tests = mapM_ test
GHCi| :}
GHCi> tests glub
"Sqrt(4) :: 2.0"
"sumList: :: 10"
一般而言,建议您在以这种方式使用存在量化之前寻找替代方案,因为它通常会带来比其值(value)更多的麻烦(有关此讨论的入门知识,请参阅例如问题和 How to convert my thoughts in OOP to Haskell? 中的所有答案)。然而,在这种情况下,您想要做的只是方便地指定要运行的测试列表,因此使用此策略似乎是明智的。在 Testing QuickCheck properties against multiple types? ,您可以看到一个与我们在这里使用 QuickCheck 所做的非常相似的示例,QuickCheck 是一个成熟的 Haskell 测试库。

关于haskell - 如何指定带有约束的类型?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40371427/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com