Почему переменная этого типа неоднозначна?
-
23-08-2019 - |
Вопрос
Капуста.hs:
module Cabbage where
class Cabbage a
where foo :: a -> String -- the parameter is only present for its type,
-- the parameter value will be ignored
bar :: String -> a
quux :: Cabbage a => String -> a
quux s = bar (s ++ foo (undefined :: a))
Когда я компилирую (с помощью ghc), я получаю это сообщение об ошибке:
Cabbage.hs:7:19:
Ambiguous type variable `a' in the constraint:
`Cabbage a' arising from a use of `foo' at Cabbage.hs:7:19-38
Probable fix: add a type signature that fixes these type variable(s)
Я не понимаю, почему a
является двусмысленным.Несомненно, что a
в строке 7 это то же самое , что и a
в строке 6?Как мне это исправить?
В качестве альтернативы, есть ли лучший способ объявить константу для каждого экземпляра?
Решение
Используя переменные типа с ограниченной областью действия, вы можете сообщить GHC, что undefined :: a
должно быть таким же (в противном случае a
это просто сокращение для forall a. a
).Переменные типа с ограниченной областью действия должны затем быть явно определены для всех:
{-# LANGUAGE ScopedTypeVariables #-}
module Cabbage where
class Cabbage a
where foo :: a -> String -- the parameter is only present for its type,
-- the parameter value will be ignored
bar :: String -> a
quux :: forall a. Cabbage a => String -> a
quux s = bar (s ++ foo (undefined :: a))
Другие советы
Проблема в том, что Haskell не знает, какой экземпляр Cabbage
это foo
соответствует там.Насколько я знаю, это не соответствует a
в (undefined :: a)
с помощью a
в quux :: Cabbage a => String -> a
Предполагая, что это то, чего вы хотите, вы можете сделать это:
quux :: Cabbage a => String -> a
quux s = result
where result = bar (s ++ foo result)
Это связывает foo и bar вместе, так что он использует один и тот же экземпляр для обоих, и поскольку вам на самом деле не нужно значение входных данных для foo, оно достигает минимума.Однако я не знаю лучшего способа создания констант для каждого экземпляра.Надеюсь, появится кто-нибудь еще, кто это сделает.
вы можете извлечь полиморфную часть в виде функции
quux :: Cabbage a => String -> a
quux s = quux' undefined
where quux' :: Cabbage a => a -> a
quux' x = bar (s ++ foo x)