从具有函数作为 Haskell 中的字段的数据类型派生 Eq 时出现问题

Problem when deriving Eq from data type with function as field in Haskell

我正在尝试从具有字段功能的数据类型派生 Eq,但没有按预期工作。

我也试过写实例但是还是不行

data Conf = Conf {
    rule :: ([Char] -> Char),
    start :: Int,
    numLines :: Double,
    window :: Int,
    move :: Int,
    actualLine :: Int,
    lastLine :: String
} deriving (Eq)

这是一个以图形方式打印wolfram金字塔的​​项目,例如,规则如下:

rule30 :: [Char] -> Char
rule30 "***" = ' '
rule30 "** " = ' '
rule30 "* *" = ' '
rule30 "*  " = '*'
rule30 " **" = '*'
rule30 " * " = '*'
rule30 "  *" = '*'
rule30 "   " = ' '
rule30 _     = ' '

有很多规则可循,因此我想将“函数指针”直接保存在Conf数据类型中。

那么,为什么我需要推导(Eq)? 我需要它,因为主要是我检查是否为 Nothing(错误处理检查,例如,如果用户设置了错误的规则...)

错误信息:

src/Wolf.hs:24:13: error:
• No instance for (Eq ([Char] -> Char))
    arising from the first field of ‘Conf’ (type ‘[Char] -> Char’)
    (maybe you haven't applied a function to enough arguments?)
  Possible fix:
    use a standalone 'deriving instance' declaration,
      so you can specify the instance context yourself
• When deriving the instance for (Eq Conf)
   |
24 | } deriving (Eq)
   |             ^^

我错过了什么?x

是什么让您认为这应该是可能的?如果您的类型包含一个函数字段,那么比较您的类型的值是否相等至少与比较函数是否相等一样困难。但是要检查两个函数是否相等(在 Haskell 中,唯一合理的含义是 extensional equality),您需要检查它们是否同意 所有可能的输入 .这是一件完全不可行的事情,即使是简单的 Int 输入,但如果参数的类型为 [Char].

So, why I need the deriving(Eq)? I need it because in the main I check if is Nothing

你完全不需要 Eq!使用 == 测试 Maybe 值是否为 Nothing 是无效的,即使在可能的类型上也是如此。您应该改为使用模式匹配

main = do
   ...
   let myConfq = ... :: Maybe Conf
   case myConfq of
     Nothing -> error "Meh, couldn't have conf"
     Just conf -> ...

...或使用更高级别的组合器,可能基于 Maybes ApplicativeTraversable 实例

import Data.Traversable

main = do
   ...
   let myConfq = ... :: Maybe Conf
   traverse ... myConfq

我正在考虑允许注释允许您想要的数据类型的字段的想法:Via fields: finer granularity in deriving

想法是定义一个比较总是成功的新类型:

newtype Ignore a = Ignore a

instance Eq (Ignore a) where
  _ == _ = True

instance Ord (Ignore a) where
  compare _ _ = EQ

然后只注释功能字段;然后当我们派生数据类型的实例时,操作字段 (==) @([Char] -> Char) 的实例实际上是通过新类型 (==) @(via Ignore):

执行的
data Conf = Conf
  { rule  :: [Char] -> Char
         via Ignore ([Char] -> Char)
  , start :: Int
  , ..
  }
  deriving
  stock (Eq, Ord)