Может ли кто-нибудь помочь мне понять, почему этот код вызывает ошибку компиляции?
hello :: (Show x) => x
hello = "Hello"
Я думал, что String
обладает свойствами Show
, поэтому функция должна возвращать String
? Вот сообщение об ошибке:
• Couldn't match expected type ‘x’ with actual type ‘[Char]’
‘x’ is a rigid type variable bound by
the type signature for:
hello :: forall x. Show x => x
at script.hs:160:1-25
• In the expression: "Hello"
In an equation for ‘hello’: hello = "Hello"
• Relevant bindings include
hello :: x (bound at script.hs:161:1)
"Hello"
— это строка. Используя сигнатуру Show x => x
, вы обещаете, что для любого x
, являющегося членом класса типов Show
, ваша функция выдаст значение, но это обещание не выполняется, поскольку вы делаете это только в том случае, если x ~ String
.
См. также Почему Num может вести себя как Fractional?.
@ Aplet123 Я просто играл с сигнатурами универсального типа, я не пытался ничего приводить. Моей единственной целью было заставить его скомпилироваться :) Но я неправильно понял, что на самом деле означают сигнатуры универсальных типов в Haskell. Сейчас прояснилось.
Ваша подпись типа не означает то, что вы думаете.
Из вашего объяснения кажется, что вы читаете эту подпись примерно так: «Я собираюсь вернуть вам значение некоторого типа x
, у которого есть экземпляр Show
. Я не буду говорить вам, какого именно типа это будет, но я обещаю, что у него будет экземпляр Show
"
Но на самом деле это означает: «выберите тип, любой тип, просто убедитесь, что он имеет экземпляр Show
, и я обещаю, что верну вам значение этого типа».
Другими словами, когда функция является универсальной, именно вызывающая функция выбирает, каким будет универсальный тип, а не реализатор.
Это имеет большой смысл. Я смешивал концепцию классов типов Haskell с интерфейсами из императивных языков, таких как Java.
Что ж, классы типов на самом деле очень похожи на интерфейсы в Java, особенно в этом конкретном случае, когда класс типов имеет один параметр. Ваша функция будет примерно эквивалентна чему-то вроде X hello<X extends IShow>()
в Java.
@FyodorSoikin, на самом деле, в данном случае они не одинаковы. Java не поддерживает полиморфизм возвращаемого типа, за исключением того, что понял ОП.
Разве это пример возвращаемого полиморфного метода @is7s?
Я не говорил, что Java вообще не поддерживает полиморфизм возвращаемого типа. Я сказал, что он поддерживает это только так, как это понял ОП. В примере, на который вы ссылаетесь, emptyList()
всегда будет возвращать конкретное значение и не зависит от контекста. Что эквивалентно 1-му определению в вашем ответе. И это не эквивалентно тому, как полиморфизм возвращаемого типа работает в классах типов.
На самом деле emptyList()
вернет значение типа, которое выбирает вызывающая сторона. Я имею в виду, что он будет обернут в List<>
, но элемент списка, который является общим параметром функции, выбирается вызывающей стороной. emptyList
сам не выбирает свой общий параметр, это делает вызывающая сторона. Это эквивалентно моему второму определению, а не первому.
В своем комментарии я сказал, что emptyList()
всегда будет возвращать конкретное значение, а не конкретный тип. Это все еще не то, как работают классы типов, и все еще эквивалентно вашему 1-му определению :)
Это означает только то, что в Java нет классов типов, т. е. разное поведение в зависимости от типов. Но у него все еще есть параметрический полиморфизм, в том числе и в возвращаемом типе. И это точно так же, как в Haskell. Сравните emptyList()
с emptyList :: [a]; emptyList = []
в Java.
Думаю, здесь мы согласны. Одно из основных различий между интерфейсами ООП и классами типов заключается в том, как они ведут себя при полиморфизме возвращаемых типов. И причиной, по которой я начал это, было ваше утверждение выше: «классы типов на самом деле очень похожи на интерфейсы в Java, особенно в этом конкретном случае, когда класс типов имеет один параметр», что не является точным. А еще я нигде не утверждал, что Java не поддерживает параметрический полиморфизм. Ваше здоровье.
Разница между типами возврата в Haskell и большинстве языков ООП также хорошо объясняется в этом ответе на аналогичный вопрос
Что вы пытаетесь достичь? Распечатать сообщение? Привести строку к любому типу
Show
? Привести любой типShow
к строке?