gpt4 book ai didi

haskell - GHC 不会选择唯一可用的实例

转载 作者:行者123 更新时间:2023-12-04 13:10:42 25 4
gpt4 key购买 nike

我正在尝试在 Haskell 中编写 CSS DSL,并使语法尽可能接近 CSS。一个困难是某些术语既可以作为属性也可以作为值出现。例如 flex: 你可以在 CSS 中使用 "display: flex"和 "flex: 1"。

我让自己受到 Lucid API 的启发,它基于函数参数覆盖函数以生成属性或 DOM 节点(有时也共享名称,例如 <style><div style="..."> )。

无论如何,我遇到了一个问题,即 GHC 无法在一个应该选择两个可用类型类实例之一的地方对代码(不明确的类型变量)进行类型检查。只有一个实例适合(实际上,在类型错误中,GHC 会打印“这些潜在实例存在:”然后它只列出一个)。我很困惑,考虑到单个实例的选择,GHC 拒绝使用它。当然,如果我添加显式类型注释,那么代码就会编译。下面的完整示例(仅依赖于 mtl,对于 Writer)。

{-# LANGUAGE FlexibleInstances #-}
module Style where

import Control.Monad.Writer.Lazy


type StyleM = Writer [(String, String)]
newtype Style = Style { runStyle :: StyleM () }


class Term a where
term :: String -> a

instance Term String where
term = id

instance Term (String -> StyleM ()) where
term property value = tell [(property, value)]


display :: String -> StyleM ()
display = term "display"

flex :: Term a => a
flex = term "flex"

someStyle :: Style
someStyle = Style $ do
flex "1" -- [1] :: StyleM ()
display flex -- [2]

和错误:
Style.hs:29:5: error:
• Ambiguous type variable ‘a0’ arising from a use of ‘flex’
prevents the constraint ‘(Term
([Char]
-> WriterT
[(String, String)]
Data.Functor.Identity.Identity
a0))’ from being solved.
(maybe you haven't applied a function to enough arguments?)
Probable fix: use a type annotation to specify what ‘a0’ should be.
These potential instance exist:
one instance involving out-of-scope types
instance Term (String -> StyleM ()) -- Defined at Style.hs:17:10
• In a stmt of a 'do' block: flex "1"
In the second argument of ‘($)’, namely
‘do { flex "1";
display flex }’
In the expression:
Style
$ do { flex "1";
display flex }
Failed, modules loaded: none.

我找到了两种编译这段代码的方法,但我都不满意。
  • 在使用 flex 函数的地方添加显式注释 ([1])。
  • 将使用 flex 的行移到 do block 的末尾(例如,注释掉 [2])。

  • 我的 API 和 Lucid 之间的一个区别是 Lucid 术语始终采用一个参数,而 Lucid 使用fundeps,这可能为 GHC 类型检查器提供了更多信息(以选择正确的类型类实例)。但在我的情况下,这些术语并不总是有论据(当它们作为值出现时)。

    最佳答案

    问题是 TermString -> StyleM () 实例仅在使用 StyleM 参数化 () 时才存在。但是在类似的do-block中

    someStyle :: Style
    someStyle = Style $ do
    flex "1"
    return ()

    没有足够的信息知道哪个是 flex "1" 中的类型参数,因为返回值被丢弃了。

    此问题的常见解决方案是 "constraint trick" 。它需要类型相等约束,所以你必须启用 {-# LANGUAGE TypeFamilies #-}{-# LANGUAGE GADTs #-} 并像这样调整实例:
    {-# LANGUAGE TypeFamilies #-}

    instance (a ~ ()) => Term (String -> StyleM a) where
    term property value = tell [(property, value)]

    这告诉编译器:“你不需要知道确切的类型 a 来获取实例,所有类型都有一个!但是,一旦确定了实例,你总是会发现类型毕竟是 ()! "

    这个技巧是亨利福特的“你可以有任何你喜欢的颜色,只要它是黑色的”的 typeclass 版本。尽管存在歧义,编译器仍可以找到一个实例,并且找到该实例为他提供了足够的信息来解决歧义。

    它之所以有效,是因为 Haskell 的实例解析从不回溯,所以一旦实例“匹配”,编译器必须提交它在实例声明的前提条件中发现的任何相等性,或者抛出类型错误。

    关于haskell - GHC 不会选择唯一可用的实例,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/41086039/

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