Я хочу создать свою собственную структуру данных списка под названием Nodes. Затем я буду использовать свой класс ListConverter, который содержит функцию 'toList', и создам его экземпляр.

data Nodes a = Empty
             | Node a (Nodes a)

class ListConverter a where
    toList :: a -> [Integer]

instance (Integral a) => ListConverter (Nodes a) where
    toList Empty = []
    toList (Node x Empty) = [x]
    toList (Node x y) = x : toList y

GHCi говорит мне, что ожидаемый тип - «Integer», но в настоящее время «a». Я очень смущен, потому что в данном случае я даю тип для (Integral). Вот сообщение об ошибке:

error:
    * Couldn't match expected type `Integer' with actual type `a'
      `a' is a rigid type variable bound by
        the instance declaration
        at main.hs:7:10-48
    * In the expression: x
      In the expression: [x]
      In an equation for `toList': toList (Node x Empty) = [x]
    * Relevant bindings include
        x :: a (bound at main.hs:9:18)
        toList :: Nodes a -> [Integer] (bound at main.hs:8:5)
  |
9 |     toList (Node x Empty) = [x]
  |                              ^
1
Kluddizz 30 Май 2019 в 02:23

2 ответа

Лучший ответ

Ожидается, что ваш экземпляр ListConverter примет любое значение класса Integral для «a», но Integer - это определенный тип, а не класс; Вы должны сделать это:

instance ListConverter (Nodes Integer) where

Это или, наоборот, сделать ваш класс ListConverter способным создавать список любого типа, который содержит значение Nodes:

class ListConverter f where
    toList :: f a -> [a]

instance ListConverter Nodes where
    toList Empty = []
    toList (Node x y) = x : toList y

(Второе уравнение для toList - (Node x Empty) - не нужно)

4
typedfern 29 Май 2019 в 23:35

Проблема с этим экземпляром очень просто заявлена. Вы дали подпись:

toList :: a -> [Integer]

Но ваш попытанный экземпляр действительно имеет тип Nodes a -> [a]. Это не работает, если a не является типом Integer - но вы утверждали, что это работает для всех Integral a. Это включает в себя другие типы, такие как Int.

Одним из решений является ограничение вашего экземпляра:

instance ListConverter (Nodes Integer) where...

Это сработает, но, на мой взгляд, не соответствует духу того, для чего вы, вероятно, предназначали класс.

Лучшее решение, которое я считаю, - это признать, что оба списка и ваш тип Nodes параматизированы другим типом, и определить класс таким образом, чтобы выполнить преобразование по общему базовому классу. Это звучит сложнее, чем есть, я просто имею в виду:

class ListConverter l where
    toList :: l a -> [a]

Затем вы можете написать instance ListConverter Nodes where... и просто скопировать существующее определение toList. (Чья средняя линия, я укажу мимоходом, является излишней.)

1
Robin Zigmond 29 Май 2019 в 23:39