#haskell
#haskell
Вопрос:
Я хочу определить сложение между двумя символами как конкатинацию, но я не уверен, как это правильно сделать.
моя попытка:
instance Num Char where
( ) (a) (b) = [a] [b]
но ошибка, которую я получаю, заключается в том, что возвращаемый тип не является ожидаемым.
Мой ожидаемый результат, как указано, представляет собой список символов, который формируется путем объединения двух символов.
Комментарии:
1.
( ) :: Num a => a -> a -> a
; вы хотите что-то с типомChar -> Char -> [Char]
.2. Даже если вы действительно придумали определение
, которое соответствует подписи, вы не можете определить
Num
экземпляр, не определив также(*)
,abs
signum
,fromInteger
negate
,(-)
и либо-либо-либо-. И если вы все же их определите, они все равно должны подчиняться различным законам, таким какa b == b a
иx fromInteger 0 == x
. Тип перегрузки оператора, предоставляемый классами типов, не является бесплатным для всех, с любым определением, предоставляемым другими языками.3. @chepner Встроенный
Natural
тип не имеет разумного определенияnegate
или(-)
, и это нарушает множество «законов» (которые на самом деле не определены в отчете).
Ответ №1:
Вместо того, чтобы перегружать ( )
нечисловой семантикой, определите свой собственный символьный оператор «сложения».
import Data.Function
-- The ^ is meant to suggest lifting values into a list.
(^ ) :: a -> a -> [a]
x ^ y = ( ) `on` pure
-- Or more simply,
-- x ^ y = [x, y]
Затем 'a' ^ 'b' == "ab"
. (^ )
будет работать для создания списка из двух элементов любого типа, а не только Char
.
> 'a' ^ 'b'
"ab"
> 3 ^ 4
[3,4]
> [1,2] ^ [4,5]
[[1,2],[4,5]]
Комментарии:
1. Показываем большие пушки, а?
on
,pure
… вы едва не пропустили обобщение( )
на(<>)
, чтобы сделать его как можно более непостижимым. Кажется, мы потеряли представление о том, насколько простым может быть Haskell.a ^ b = [a, b]
(В любом случае проголосовал против.)2. Извините, я выполнил то, что, как я думал, было предложением по редактированию, но в итоге фактически отредактировал ответ; мой SO немного подзабыл. Я согласен с основной идеей ответа, но предложил более разумную реализацию
^
. Не стесняйтесь редактировать обратно, если вам кажется, что исходная реализация более разумна.3. @JustinL. У вас достаточно репутации, чтобы ваши правки не нуждались в экспертной оценке: ожидается, что вы достаточно хорошо знаете, как все работает, и не вносите бессмысленных правок. Показательный пример: эта правка была абсолютно разумной.
4. Я бы предпочел дополнение к ответу, а не полное исключение
on
решения.
Ответ №2:
Это невозможно. ( )
Функция в Num
имеет тип a -> a -> a
, поэтому возвращаемый тип должен совпадать с типом параметра. Вы не можете добавить два символа и получить строку в результате. В более общем плане, вы не должны реализовывать Num для вещей, отличных от арифметики над числами.
Комментарии:
1. Я понимаю, но если бы я взял две строки в качестве входных данных и объединил их, другими словами, обозначение типа [Char] -> [Char] -> [Char], тогда было бы возможно определить ( ) для [Char] ?
2. Компилятор не будет жаловаться, но любой человек, читающий вашу программу, будет жаловаться.
3. В документах для
Num
указано несколько законов, которым должны удовлетворять экземпляры, и объединение строк им не подходит. Например, конкатенация строк не удовлетворяла бы коммутативности сложения. Компилятор не будет возражать, но такой экземпляр будет иметь странное поведение, неочевидное для других пользователей, и, таким образом, приведет к незначительным ошибкам.4. Кроме того, мне сейчас приходит в голову, что компилятор будет жаловаться. Вы должны определить экземпляр для
[a]
в целом и не можете специализироваться только на[Char]
. Вы могли бы определить сложение для списков как конкатенацию, но, конечно, это было бы широко осуждено.