【发布时间】:2021-06-28 14:17:30
【问题描述】:
这段代码编译:
import Data.List (isPrefixOf)
import Data.Monoid (Any(..))
import Data.Coerce
isRoot :: String -> Bool
isRoot path = getAny $ foldMap (coerce . isPrefixOf) ["src", "lib"] $ path
我使用coerce 作为将isPrefixOf 的最终结果包装在Any 中的快捷方式。
这个类似的代码无法编译(注意缺少.):
isRoot :: String -> Bool
isRoot path = getAny $ foldMap (coerce isPrefixOf) ["src", "lib"] $ path
错误是:
* Couldn't match representation of type `a0' with that of `Char'
arising from a use of `coerce'
* In the first argument of `foldMap', namely `(coerce isPrefixOf)'
In the first argument of `($)', namely
`foldMap (coerce isPrefixOf) ["src", "lib"]'
In the second argument of `($)', namely
`foldMap (coerce isPrefixOf) ["src", "lib"] $ path'
但我的直觉是它也应该编译。毕竟,我们知道isPrefixOf 的参数将是Strings,并且结果必须是Any 类型。没有歧义。所以String -> String -> Bool 应该转换为String -> String -> Any。为什么它不起作用?
【问题讨论】:
-
这与
isRoot = or . sequence (map isPrefixOf ["src", "lib"])有什么不同吗?只是好奇。 -
@jpmarinier 是等价的。虽然代码有点人为,但我的问题的重点是
coerce的问题。 -
MVCE:
coerce id :: Int -> Int产生同样的问题,但coerce (id :: Int -> Int) :: Int -> Int不会。所以与多态性有一些相互作用。 -
我的猜测是
coerce isPrefixOf,不像coerce . isPrefixOf,也会影响isPrefixOf的输入。如果我们有newtype CaseInsensitive = CI Char和自定义Eq实例,那么coerce isPrefixOf可能意味着\x y -> coerce (isPrefixOf x y)或\x y -> coerce (isPrefixOf (map CI x) (map CI y))具有不同的语义。因此代码是模棱两可的(?) -
@danidiaz - 谢谢。顺便说一句,您似乎通过制作“类型孔”获得了某种提示,即您将源代码中的
coerce替换为_coerce。似乎编译器被isPrefixOf的多态性引入了歧途。不知道为什么。如果你使用(coerce (isPrefixOf :: String -> String -> Bool)),它会进行类型检查。同样,如果启用了TypeApplicationsGHC 扩展,使用(coerce (isPrefixOf @Char))也可以正常工作。