解决重载函数的歧义问题

4
我希望在Haskell中有一个重载函数。
{-# LANGUAGE FlexibleInstances #-}
class Foo a where
   foo :: a

instance Foo (String -> Int) where
   foo = length

instance Foo String where
   foo = "world"

然而,这种过载对于类型模糊性处理非常糟糕。print $ foo "hello"将导致错误,而print $ length "hello"却可以正常工作。但是,只要我的实例列表固定,Haskell就没有技术上的障碍来认识到foo :: String -> a的唯一实例是foo :: String -> Int。我能让Haskell做出这个认识吗?


这真的是你想做的吗?但对于这个问题,我不认为它可以被普遍实现。 - Random Dev
@Carsten,我可以接受使用函数fooLenfooStr,但我认为重载更加方便。 - Karolis Juodelė
2
问题在于,如果您导出了函数(例如printFoo),然后如果有人使用它并添加了一个实例instance Foo (String -> Double),那么他会遇到麻烦。 - Random Dev
这也可能是YAGNI的情况 - 不要过快地引入类型类(当然只是我的个人意见)。 - Random Dev
3
“… 前提是我的实例列表已经固定,…” - 但类型类是开放式的 - 也就是说,您稍后可以在另一个模块中添加一个实例。” - ErikR
@Carsten,这就是关键。任何可能的解决方案都可能涉及防止类被扩展。 - Karolis Juodelė
2个回答

4
在这种情况下,操作很简单。只需要:
instance a ~ Int => Foo (String -> a) where foo = length

2
+1 当然 我没想到这一点 - 不错的变通方法 - 当然,我仍然认为像 Foo 这样的类只是另一个问题的症状 ;) - Random Dev
作为旁注,仅限制为Num并使用genericLength也可以:instance Num a => Foo (String -> a) where foo = genericLength。也许这更容易理解,因为我认为相等约束并不被广泛知道(至少对于初学者来说是这样)。 - Random Dev
太好了。虽然我不得不添加IncoherentInstances才能使第二个实例工作。我在哪里可以阅读为什么这样做有效的相关信息? - Karolis Juodelė
@KarolisJuodelė,你绝对不应该需要不连贯的实例,并且即使你认为你需要它,也不应该打开它。第二个实例是什么?在你的问题中给出的这两个实例对我来说都可以正常工作,而无需不连贯的实例。 - Daniel Wagner
@DanielWagner,那我可能改错了。我做了 instance a ~ String => Foo a where foo = "world"一个有不连贯实例的工作版本以及没有它失败的相同代码 - Karolis Juodelė
显示剩余3条评论

0

在你的情况下,GHCI知道foo :: String -> ??

我们将把签名更改为String -> Int

print (foo "hello" :: Int)

1
我认为问题是为什么需要这个(如果你能让编译器自己找到它)。 - Random Dev

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接