gpt4 book ai didi

haskell - 派生包含数据结构时未使用类型类实例

转载 作者:行者123 更新时间:2023-12-02 16:17:03 24 4
gpt4 key购买 nike

我一直在探索在代码中使用更多 newtype 包装器来创建更多不同的类型。我还使用 Read/Show 进行许多廉价的序列化,特别是作为强类型配置文件的简单形式。我今天遇到了这个:

示例是这样开始的,我定义了一个简单的新类型来包装 Int,以及一个用于展开的命名字段:

module Main where

import Debug.Trace ( trace )
import Text.Read ( readEither )


newtype Bar = Bar { unBar :: Int }
deriving Show

自定义实例,用于从简单的 Int 语法读取其中之一。这里的想法是,如果能够将“42”而不是“Bar { unBar = 42 }”放入配置文件中,那就太好了

该实例还具有跟踪“日志记录”,因此我们可以在观察问题时看到该实例何时真正被使用。

instance Read Bar where
readsPrec _ s = [(Bar i, "")]
where i = read (trace ("[debug \"" ++ s ++ "\"]") s)

现在另一种类型包含一个 Bar。这个只会自动派生 Read。

data Foo = Foo { bar :: Bar }
deriving (Read, Show)


main :: IO ()
main = do

单独反序列化 Bar 类型效果很好,并使用上面的 Read 实例

   print $ ((readEither "42") :: Either String Bar)
putStrLn ""

但是由于某种原因,包含 Bar 并自动派生为 Read 的 Foo 并没有向下钻取并获取 Bar 的实例!(请注意,来自跟踪的调试消息也不会显示)

   print $ ((readEither "Foo { bar = 42 }") :: Either String Foo)
putStrLn ""

那么好吧,Bar 的默认 Show 表单应该与默认的 Read right 相匹配吗?

   print $ ((readEither "Foo { bar = Bar { unBar = 42 } }") :: Either String Foo)

不!也不行啊!!同样,没有调试消息。

这是执行输出:

  $ stack exec readbug
[debug "42"]
Right (Bar {unBar = 42})

Left "Prelude.read: no parse"

Left "Prelude.read: no parse"

这对我来说看起来有问题,但我很想知道我做错了。

上面的代码有一个完整的工作示例。请参阅 test project on darcshub 中的文件 src/Main.lhs

最佳答案

问题出在 ReadreadsPrec需要考虑在 Bar 之后可能会看到更多内容的可能性。 Quoting the Prelude :

readsPrec d s attempts to parse a value from the front of the string, returning a list of (<parsed value>, <remaining string>) pairs. If there is no successful parse, the returned list is empty.

就您而言,您想要:

instance Read Bar where
readsPrec d s = [ (Bar i, s') | (i, s') <- readsPrec d tracedS ]
where tracedS = trace ("[debug \"" ++ s ++ "\"]") s

然后,进行以下工作:

ghci> print $ ((readEither "Foo { bar = 42 }") :: Either String Foo)
[debug " 42 }"]
Right (Foo {bar = Bar {unBar = 42}})

您的其他问题,即:

So ok, how about the default Show form for Bar, should match the default Read right?

 print $ ((readEither "Foo { bar = Bar { unBar = 42 } }") :: Either String Foo)

是你的错:你定义了 Read Bar 的实例这样read . show不是身份操作。当Foo派生Read ,它使用 Bar Read实例(它不会尝试重新生成 Bar 如果您派生 Read 就会生成的代码)。

关于haskell - 派生包含数据结构时未使用类型类实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41176222/

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