#haskell
#хаскелл
Вопрос:
У меня есть два класса типов
class Concatable a where
empty :: a
(< >) :: a -> a -> a
class Concatable b => Output a b where
out :: a -> b
и следующая функция
nl :: (Output a AnsiDark) => [a] -> AnsiDark
nl a = foldr addNl empty a
where
addNl :: a -> AnsiDark -> AnsiDark
addNl ast org = doAddIf ast < > org
doAddIf :: a -> AnsiDark
doAddIf ast = if out ast == sDedent
then out ast
else out ast < > sNewline
( AnsiDark
реализует Concatable
, sDedent
является константой типа AnsiDark
)
а также включены следующие языковые расширения (которые могут даже не иметь отношения к проблеме, я довольно новичок в этих сложных проблемах, связанных с типом)
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
и я получаю следующую ошибку:
../src-hs/ANSITerm.hs:65:22: error:
• Could not deduce (Output a1 AnsiDark) arising from a use of ‘out’
from the context: Output a AnsiDark
bound by the type signature for:
nl :: forall a. Output a AnsiDark => [a] -> AnsiDark
at ../src-hs/ANSITerm.hs:59:1-44
• In the first argument of ‘(==)’, namely ‘out ast’
In the expression: out ast == sDedent
In the expression:
if out ast == sDedent then out ast else out ast < > sNewline
Я не совсем понимаю, почему хаскелл не может вывести a
… Я бы прибегнул к использованию аннотаций out
типов примерно так
out @a @AnsiDark
но аннотации типов, похоже, не работают с переменными типа. Итак … в чем именно моя проблема здесь? И как я мог это решить?
Комментарии:
1.
(Output a1 AnsiDark)
является ограничением. Компилятор пытается сопоставить это с экземпляром forOutput
, чтобы он знал, как реализоватьout
. Похоже, у вас вообще нет экземпляров. Что вы ожидаетеout ast
получить, если вы не дали для этого уравнения?2. Сообщение, которое вы получите от компилятора Hugs, будет
Unresolved overloading
. Это было бы лучшим объяснением? Это не «проблема, связанная со сложным типом». (Сообщения GHC в целом ожидают, что вы пытаетесь сделать что-то действительно необычное. Они слишком полны головокружительной теории типов для новичков IMO.)
Ответ №1:
nl :: (Output a AnsiDark) => [a] -> AnsiDark
...
where
doAddIf :: a -> AnsiDark
...
Буквы a
s, которые появляются в этих двух строках, не совпадают. Это как если бы вы написали:
nl :: (Output x AnsiDark) => [x] -> AnsiDark
...
where
doAddIf :: y -> AnsiDark
...
Поскольку вы используете out
in doAddif
, вам нужно добавить Output
ограничение к его подписи (я считаю, что это сработает, если вы удалите подпись, поскольку будет выведена правильная подпись).
Вас также может заинтересовать ScopedTypeVariables
расширение. Если это включено, если вы напишете
nl :: forall a. (Output a AnsiDark) => [a] -> AnsiDark
затем вы можете ссылаться на это a
в подписях в where
предложении, а также в приложениях типа, подобных тому out @a @AnsiDark
, который вы пробовали.