如何在 Haskell 中工作的返回类型上获得“不可预测的”重载?

Posted

技术标签:

【中文标题】如何在 Haskell 中工作的返回类型上获得“不可预测的”重载?【英文标题】:How do I get 'unpredictable' overloading on a return type working in Haskell? 【发布时间】:2019-05-19 03:28:40 【问题描述】:

我有一些类型实例。我们称它们为 A、B 和 C。它们都是类型类 X 的实例。现在我想创建一个单独的函数 create,它在给定一些输入(假设是一个字符串)的情况下创建 A、B 或 C 的实例。类型系统无法知道什么输入将给出什么类型。那是 Haskell 不喜欢的东西,我想我知道答案,但我想确定一下。我得到的当前错误是:

• Couldn't match expected type ‘c’ with actual type ‘GCCCommand’
  ‘c’ is a rigid type variable bound by
    the type signature for:
      compiler :: forall c. CompilerCommand c => String -> c
    at src/System/Command/Typed/CC.hs:29:1-44
• In the expression: gcc path
  In an equation for ‘compiler’:
      compiler path
        | exe == "g++" || exe == "gcc" || exe == "cc" || exe == "cpp"
        = gcc path
        where
            exe = takeFileName path
• Relevant bindings include
    compiler :: String -> c
      (bound at src/System/Command/Typed/CC.hs:31:1)

这是否意味着,正如我所怀疑的,在这种特殊情况下不可能重载返回类型,因为编译器无法预先知道数据在内存中的外观?你将如何实现这个功能?我正在考虑创建如下内容:

data SuperX = SuperA A | SuperB B | SuperC C

create :: String -> SuperX
-- create can now be implemented

instance X SuperX where
  -- a lot of boilerplate code ...

但是,样板代码表明它可以做得更好。这真的是最好的方法吗?

【问题讨论】:

sum 类型,例如SuperX,是最常见但并不总是“最佳”解决方案。什么是最好的很大程度上取决于你如何消费这些价值观以及个人风格。例如,您可以尾调用计算的其余部分,例如 createAndApply :: (A -> x) -> (B -> x) -> (C -> x) -> String -> x。你也可以使用一些动态类型,但这很少被认为是“最好的”。 感谢您的评论!我不知道动态类型,但这似乎确实有点矫枉过正。好像会变成SuperX 我认为我们需要更多细节。例如,这三种类型实际上是三种(而不是十种)? X 类中大概有多少方法?您是否仅通过此类的方法访问返回值?有一种方法可以使用存在类型返回“某个类型类的某个实例”,但这通常被认为是antipattern,所以我们在这里要小心。 @chi 感谢您的评论。如果您有兴趣,可以找到源代码here。我几乎用完了SuperX-方法,但我愿意接受改变。 我看了一眼,但很难说。 SuperX 方法可能风险较小。 【参考方案1】:

这取决于你需要用它做什么。

如果您以后的处理不关心它是否获得ABC,只是它获得了实现X 的东西...

restOfProgram :: X a => a -> ThingIWantToCompute

那么你可以使用延续传递:

parseABC :: (X a => a -> r) -> String -> Maybe r
parseABC f "A" = Just (f A)
parseABC f ('B':xs) = Just (f (B xs))
parseABC f ('C':xs) = Just (f (C (read xs)))
parseABC _ _ = Nothing

或存在的数据包装器:

data SomeX where
  SomeX :: X t => t -> SomeX

parseABC :: String -> Maybe SomeX
parseABC "A" = Just (SomeX A)
parseABC ('B':xs) = Just (SomeX (B xs))
parseABC ('C':xs) = Just (SomeX (C (read xs)))
parseABC _ _ = Nothing

restOfProgram' :: SomeX -> ThingIWantToCompute
restOfProgram' (SomeX t) = restOfProgram t

如果后面的处理对于 ABC 有不同的路径,您可能希望返回一个 sum 类型,如 SuperX

【讨论】:

感谢您的广泛回答!我将使用您建议的不同类型的解决方案;会告诉你进展如何。

以上是关于如何在 Haskell 中工作的返回类型上获得“不可预测的”重载?的主要内容,如果未能解决你的问题,请参考以下文章

创建在 IE 中工作的文件上传

无法获得指向在 Gambit-C 方案的 FFI 中工作的指针的指针

用于在Firefox中工作的网站的Facebook Messenger,而不是Chrome

关于管道如何在 Bash 中工作的简单解释是啥?

如何在 iPhone 模拟器中工作的 UIAutomation 中获取 captureScreenWithName?

sdcc 在 keil 中工作的代码上给出语法错误