Почему haskell не может вывести этот класс с несколькими параметрами?

#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-44In 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) является ограничением. Компилятор пытается сопоставить это с экземпляром for Output , чтобы он знал, как реализовать 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 , который вы пробовали.