Литерал OverloadedLists верхнего уровня

У меня есть набор тестов для рефакторинга, и я бы хотел, чтобы он был совместим с обоими Data.List а также Data.List.NonEmpty. Упражнение состоит из функцииfoo :: [Foo] -> Foo и в наборе тестов есть

data Case = Case
  { description :: String
  , input :: [Foo]
  , expected :: Foo
  }

cases :: [Case]
cases =
  [ Case { description = "blah blah"
         , input = [Foo 1, Foo 2, Foo 3]
         , expected = Foo 1
         }
  , ...
  ]

Чтобы сделать набор тестов полиморфным с OverloadedLists, Я попытался

{-# LANGUAGE OverloadedLists #-}
...

data Case list = Case
  { description :: String
  , input :: list Foo
  , expected :: Foo
  }

cases =
  [ Case { description = "blah blah"
         , input = [Foo 1, Foo 2, Foo 3]
         , expected = Foo 1
         }
  , ...
  ]

но это дает мне ошибку

    • Couldn't match expected type ‘GHC.Exts.Item (list0 Foo)’
                  with actual type ‘Foo’
      The type variable ‘list0’ is ambiguous
      ...
   |
50 |          , input = [Foo 1, Foo 2, Foo 3]
   |                     ^^^^^

Я думал переместить IsList list ограничение на Case тип данных, вот так

{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedLists #-}
...

data Case list where
  Case :: IsList list => String -> list Foo -> Foo -> Case list

cases =
  [ Case "blah blah" [Foo 1, Foo 2, Foo 3] (Foo 1), ... ]

но это дает мне ошибку

    • Expected kind ‘* -> *’, but ‘list’ has kind ‘*’
    • In the type ‘list Foo’
      In the definition of data constructor ‘Case’
      In the data declaration for ‘Case’
   |        
24 |   Case :: IsList list => String -> list Foo -> Foo -> Case list
   |                                    ^^^^^^^^

Я не уверен, какой здесь самый простой подход. Есть подсказки?

1 ответ

Решение

Причина, по которой это не работает, заключается в том, что Item тип List (l Foo) => lне сам по себе Foo. Расширение делает это абстракцией и, следовательно, ожидает, что элементы вашего литерала списка будут иметь типItem (l Foo).

Однако вы можете добавить ограничение типа, которое говорит, что элементы действительно имеют тип Foo:

{-# LANGUAGE OverloadedLists #-}

data Case list = Case
  { description :: String
  , input :: list Foo
  , expected :: Foo
  }

cases :: (IsList (l Foo), Item (l Foo) ~ Foo) => [Case l]
cases = [
    Case { description = "blah blah"
         , input = [Foo 1, Foo 2, Foo 3]
         , expected = Foo 1
         }
  ]

Таким образом, мы говорим, что Item (l Foo) должно быть таким же, как Foo.

Другие вопросы по тегам