LINUX.ORG.RU

Хаскель - несколько определений одной функции - порядок применения

 , ,


0

3

Вот программка на Хаскеле

sayMe :: (Integral a) => a -> String

sayMe x
    | x <= 2 = "Less then three!"

sayMe 1 = "One!"

main = putStrLn (sayMe 1)
-- результат  зависит от порядка определений
Здесь печатает «Less then three». Если поменять определения местами, то напечатается «One!». В явном виде я нигде не прочитал, но похоже, что определения обрабатываются по порядку, и первое подходящее применяется.

Эта штука мне нравится, поскольку по сути это тот «полиморфизм», который гениально прост и при этом не налагает никаких требований на систему типов. В С++ или в CLOS методы выбираются не по порядку определения, а по отношению «предок-потомок», или, иными словами «тип-подтип». Это отношение не всегда легко определить.

Но у меня вопросы:

1. Могу ли я размазать определение sayMe по нескольким модулям, ничего не знающим друг про друга?

2. Если да, то как при этом выстраивается порядок определений? Ведь обычно при сборке мы не точно знаем порядок впихивания модулей в готовый исполняемый файл.

3. Есть ли способ воткнуть своё определение перед первым определением, задав свой частный случай?

★★★★★

Последнее исправление: den73 (всего исправлений: 1)

Насколько я знаю, определение функции должно полностью содержаться в одном модуле. Для частного случая можно разве что определить sayMe', которая будет его обрабатывать, а в остальных случаях делегировать в sayMe.

Softwayer ★★
()

1. Могу ли я размазать определение sayMe по нескольким модулям, ничего не знающим друг про друга?

Нет.

3. Есть ли способ воткнуть своё определение перед первым определением, задав свой частный случай?

В таком виде - нет. Ты скорее всего хочешь использовать тайпклассы и newtype для определения нескольких инстансов.

hateyoufeel ★★★★★
()
Последнее исправление: hateyoufeel (всего исправлений: 1)

1. В haskell не бывает нескольких определений функции. 2. В данном случае это список pattern matches, и проходятся сверху вниз и выбирается первый совпадающий. 3. Советую читать haskell report, про case и pattern-matching . 4. Это ни в какой мере не полиморфизм, в каком-либо из его видно. 5. нельзя размазать по нескольким модулям, даже по одному нельзя:

foo x = error "boo"
bar y = error "bar"
foo _ = error "qqu"
нельзя 6. см 6 7. для этого тебе нужнен ad-hoc полиморфизм и классы типов, там то как раз можно сделать все что ты хочешь: и полиморфизм и раскидать по модулям и сделать дефолтный метод. Напр:
-- file A.hs
class MyEq a where
  a `eq` b = not (neq a b) -- default
  a `neq` b = not (eq a b) -- default
-- file B.hs
import A
intance MyEq Foo where ..
-- file C.hs
import A
instance MyEq Bar where ..

qnikst ★★★★★
()

Эта штука мне нравится, поскольку по сути это тот «полиморфизм», который гениально прост

Что то не понял, где тут полиморфизм?

portquest2016
()

обрабатываются по порядку,

это, кстати, хорошая тема для размышлений по-поводу того, что в ФП «нет состояния и времени»

portquest2016
()
Ответ на: комментарий от portquest2016

анониймус, уйди, пожалуйста из haskell тредов.

qnikst ★★★★★
()

В С++ или в CLOS методы выбираются не по порядку определения, а по отношению «предок-потомок», или, иными словами «тип-подтип». Это отношение не всегда легко определить.

Ты вообще все попутал. Это вообще данного случая не касается, это иерархия, тут у тебя никакой иерархии нет, где ты увидел ее? И вообще, то что ты называешь выбором, в CLOS и в большинстве нормальных языков выбирается в рантайме, а не на этапе определения, как в твоем примере. Как ты умудряешься язык писать, если ты в элементарных понятиях путаешься?

portquest2016
()

Это отношение не всегда легко определить.

А в чем проблема определить это, lol?

portquest2016
()

Всем спасибо!

den73 ★★★★★
() автор топика

поскольку по сути это тот «полиморфизм»

это всего лишь сахарок для case, примерно: sayMe x = case x of _ | x <= 2 -> «smth»; 3 -> «one»

anonymous
()
Ответ на: комментарий от anonymous

Поскольку это всех «зацепило», поясню свою точку зрения: в математике, да и в CL, можно ввести сколько угодно подтипов целых чисел:

CL-USER>(typep 32 '(integer 0 34))
T
CL-USER>(typep 32 '(integer 0 30))
NIL
CL-USER>(subtypep '(integer 0 30) 'integer)
T
С этой точки зрения «ровно число 1» и «целое число меньше трёх» - это два подтипа целых чисел. А значит, можно смотреть на мой пример как на определение двух «методов» для двух разных подтипов целых чисел, т.е. это полиморфизм.

Но, как оказалось, этот механизм не для того.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

то есть, ты хотел чтобы в статическом языке типы выводились по предикатам в рантайме?

portquest2016
()

Когда результат зависит от порядка кляуз, это уже какая-то тухлая деклсративность. Посмотри язык Curry, там такого нет.

anonymous
()

Я так понимаю, порядок определения clause'ов должен следовать правилу «от более точного паттерна к более общим». Именно поэтому последним паттерном служит обычно функция otherwise. А то, что у тебя разные предикаты перекрывают частично друг друга, то это «сам дурак» ).

Virtuos86 ★★★★★
()
Ответ на: комментарий от Virtuos86

нет, они банально сверху вниз, а otherwise банально синоним для истины

AndreyKl ★★★★★
()
Ответ на: комментарий от anonymous

Когда результат зависит от порядка кляуз, это уже какая-то тухлая деклсративность. Посмотри язык Curry, там такого нет.

Ты имеешь в виду здесь страницу 23?.

Но это просто записано другое по смыслу. Насколько я понял, там записан набор правил, которые действуют «все одновременно», т.е. независимо от порядка, нужно применить все. А я вполне осознанно хочу написать набор правил, которые обрабатываются по порядку. Ничего «тухлого» тут нет. И те, и другие имеют право на существование.

Я без понятия что оно делает в zeromq4 и есть ли отдельный пакет.

Не суть важно, целые числа здесь для примера.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

[офтоп]Ну Haskell у нас декларативным зовется, а в итоге что-то абстракция протекает.

В Curry кляузы применяются параллельно, если ты допустим в первой уйдешь в бесконечную рекурсию, но у тебя есть другая кляуза которая подходит и завершается, тогда она и будет результатом. А Haskell из-за последовательного раскрытия так и завязнет в рекурсии :-( Нельзя писать как хочется, надо следить за порядком кляуз.

Еще казалось бы типы решают, а нет, можно например словить ошибку в рантайме, попробовав вытащить голову пустого списка. А в Idris с зависимыми типами например, за такое даст по рукам компилятор.

Однако трудно вместить все концепции, не вызвав противоречий, поэтому Haskell таков как есть.[/офтоп]

А по делу тебе верно сказали, не забивай гвозди микроскопом. Прочти книжку по Haskell, желательно временно забыв идеи из других языков и впитай его идеи. Свой язык можешь дизайнить как тебе нравится, но в других ты пользователь, и писать на них следует согласно руководству :-)

anonymous
()
Ответ на: комментарий от anonymous

в итоге что-то абстракция протекает.

Протекает только тормозная жидкость из твоей бестолковки.

В Curry кляузы применяются параллельно, если ты допустим в первой уйдешь в бесконечную рекурсию, но у тебя есть другая кляуза которая подходит и завершается, тогда она и будет результатом. А Haskell из-за последовательного раскрытия так и завязнет в рекурсии

Идиот, если у тебя для какого-то значения X одна функция вычислилась в Y ≠ ⊥, а другая «завязла в рекурсии», т.е. вычислилась в ⊥, то это тупо РАЗНЫЕ функции, по самому наивному определению (не)равенства функций.

Нельзя писать как хочется, надо следить за порядком кляуз.

Функция из A в B в теоретико-множественном смысле это подмножество произведения A×B со специальными свойствами.

Это было определение. Всё остальное — это способы задания/описания функции. В том числе «с помощью кляуз». И если ты не разобрался в том, как пользоваться этим способом, это только твоя вина. Если ты задал не ту функцию, которую хотел, то это только твоя вина.

А в Idris с зависимыми типами например, за такое даст по рукам компилятор.

Твои понты зависимыми типами ещё более смешны на фоне того, что ты не разбираешься в элементарных вещах.

anonymous
()
Ответ на: комментарий от anonymous

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

Ну, в идеальном мире это так и будет, наверное. А в реальном наверняка элементарно конструируется правило, у которого на каждом шаге рекурсии плохой ветки она fork-ает из себя ещё одну безконечно рекурсивную. И результат, хотя и теоретически достижим, на практике никогда не будет достигнут (закончатся ресурсы компьютера).

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

Может быть, в другой раз. Я пока так и не понял, зачем мне Хаскель нужен. Широко разрекламированные тайпклассы оказались банальными интерфейсами. Сохранять состояние вычисления в контейнер с последующим перезапуском из контейнера тоже оказалось нельзя, невзирая на хвалёные монады. Широкого промышленного применения язык не получил.

Еще казалось бы типы решают, а нет, можно например словить ошибку в рантайме

Как там... Теорема Гёделя?

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

Так, началась битва анонимусов. Я пошёл отсюда.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

С этой точки зрения «ровно число 1» и «целое число меньше трёх» - это два подтипа целых чисел. А значит, можно смотреть на мой пример как на определение двух «методов» для двух разных подтипов целых чисел, т.е. это полиморфизм.

Для такого лучше смотри GLS

monk ★★★★★
()
Ответ на: комментарий от den73

Широко разрекламированные тайпклассы оказались банальными интерфейсами.

Лол...

anonymous
()
Ответ на: комментарий от den73

Сохранять состояние вычисления в контейнер с последующим перезапуском из контейнера тоже оказалось нельзя, невзирая на хвалёные монады.

В смысле? Монаду ST вполне можно хранить. А она хранит как раз состояние вычисления (которое внутри этой монады).

monk ★★★★★
()
Ответ на: комментарий от anonymous

Когда результат зависит от порядка кляуз, это уже какая-то тухлая деклсративность.

«Когда результат зависит от расстановки скобок, это уже какая-то тухлая деклсративность.»

anonymous
()
Ответ на: комментарий от den73

Широкого промышленного применения язык не получил.

В сравнении с Common Lisp?

hateyoufeel ★★★★★
()
Ответ на: комментарий от den73

Я пока так и не понял, зачем мне Хаскель нужен.

Парадокс Блаба, которым так любят потрясать скобканутые, когда их спрашивают «зачем нужен лисп?», ударил по самим скобканутым.

Широко разрекламированные тайпклассы оказались банальными интерфейсами.

«Парадокс Блаба» описан в очерке «Побеждая посредственность». Его сущность состоит в том, что программист, знающий некоторый язык («Блаб»), «думает на Блабе»

Java-макаке везде мерещатся «интерфейсы».

anonymous
()
Ответ на: комментарий от den73

Широко разрекламированные тайпклассы оказались банальными интерфейсами.

нет.

Сохранять состояние вычисления в контейнер с последующим перезапуском из контейнера тоже оказалось нельзя, невзирая на хвалёные монады.

не знаю при чем тут монады и почему нельзя сохранить состояние в контейнер и перезапустить его оттуда. Если приведёшь пример задачи, то покажу как сделать.

Широкого промышленного применения язык не получил.

По сравнению с java не получил, по сравнению с CL получил.

qnikst ★★★★★
()
Ответ на: комментарий от anonymous

Ты чего так распсиховался, таблетки выпить забыл? :-)

Идиот, если у тебя для какого-то значения X одна функция вычислилась в Y ≠ ⊥, а другая «завязла в рекурсии», т.е. вычислилась в ⊥, то это тупо РАЗНЫЕ функции, по самому наивному определению (не)равенства функций.

Функция одна - одно имя, один возвращаемый тип (поскольку дно недостижимо) :-)

anonymous
()
Ответ на: комментарий от anonymous

Еще казалось бы типы решают, а нет, можно например словить ошибку в рантайме, попробовав вытащить голову пустого списка.

Ты безграмотен, учись старательнее.

anonymous
()
Ответ на: комментарий от anonymous

Когда результат зависит от расстановки скобок, это уже какая-то тухлая деклсративность

Не по делу ты ляпнул. Но в хаскеле есть бесточечная нотация

anonymous
()
Ответ на: комментарий от den73

Ну, в идеальном мире это так и будет, наверное. А в реальном наверняка элементарно конструируется правило, у которого на каждом шаге рекурсии плохой ветки она fork-ает из себя ещё одну безконечно рекурсивную. И результат, хотя и теоретически достижим, на практике никогда не будет достигнут (закончатся ресурсы компьютера).

Там параллельный проход + ограничение числа получаемых значений. Конечно, если попросишь все значения, то программа зациклится также как на Хаскеле. Похоже на Пролог, но там как и в Хаскеле линейный проход, так что программа сразу циклится.

anonymous
()
Ответ на: комментарий от monk

Монаду ST вполне можно хранить. А она хранит как раз состояние вычисления (которое внутри этой монады).

Да, уже разбирали этот вопрос и, наверное я неправ Но пусть. Тема о другом.

Для такого лучше смотри GLS

Угу, похоже на то, что надо. Это можно обсудить отдельно.

den73 ★★★★★
() автор топика
Ответ на: комментарий от qnikst

Спасибо, не надо. Это тема вполне конкретная, и я не хочу переходить на обсуждение достоинств или недостатков Хаскеля. Я всего лишь вежливо объяснил, почему я в ближайшем будущем не собираюсь читать книгу по Хаскелю, «забыв на время всё, что я знал до этого».

den73 ★★★★★
() автор топика
Последнее исправление: den73 (всего исправлений: 1)
Ответ на: комментарий от anonymous

Там параллельный проход + ограничение числа получаемых значений.

Ты не понял мой пример. Запускаем ветвящееся дерево вычислений, из которых только одно придёт к результату, а остальные - это экспоненциальный fork bomb. fork bomb растворит в себе успешную ветку, и она не успеет завершиться до исчерпания ресурсов компьютера.

den73 ★★★★★
() автор топика
Ответ на: комментарий от anonymous

А в случае, когда просто перебираем N вариантов без всяких рекурсий и за фиксированное время получаем результат, мы не выходим на такую проблему. Так что зависимость от порядка правил и вообще примитивизм языка описания - не всегда минус.

den73 ★★★★★
() автор топика
Последнее исправление: den73 (всего исправлений: 1)
Ответ на: комментарий от anonymous

Проблема надумана. Тьюринговская полнота не нужна, только и всего.

GoodRiddance
()
Ответ на: комментарий от den73

я к тому, что объяснение мягко-говоря странное и в нём 1 ложный постулат, и один странный, постутал про распространённость нормальный.

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

Ну, как сказал батюшка трудникам, откусывая в пост от бутерброда с бужениной, «за свои грехи каждый сам отвечает». Думаю, тебя устроит, если я добавлю ко всем моим утверждениям по Хаскель слова «в меру моих незнаний»? А далее это же моя жизнь, мне её и жить. Я ещё повыясняю, что хорошего есть в Хаскеле - наверное оно там и вправду есть, раз так многие его хвалят. Но не сейчас.

den73 ★★★★★
() автор топика
Последнее исправление: den73 (всего исправлений: 1)
Ответ на: комментарий от den73

Ну, я учить не заставляю. И спорить и рассказывать о преимуществах не собираюсь. А вот поправить некоторые заблуждения это можно, но не нужно - так не нужно.

qnikst ★★★★★
()
Ответ на: комментарий от anonymous

Там нет скобок? Или результат не зависит от их расстановки?

anonymous
()
Ответ на: комментарий от den73

Но не сейчас.

Вот и отлично. Избегай только ставить тег haskell на свои треды.

GoodRiddance
()
Ответ на: комментарий от qnikst

Если ты хочешь объяснить отличие тайпклассов от интерфейсов, я всегда за, просто мне несколько стыдно тебя эксплуатировать - я на них лишь мельком глянул.

Касабельно сохранения состояния, даже если этот вопрос как-то и решается, на сегодня он для меня неактуален, и кроме того, я уже признал, что ошибся.

den73 ★★★★★
() автор топика
Ответ на: комментарий от den73

Для классов типов работает вывод инстанса по возвращаемому значению (умными словами сказать не могу, но эта одна из основных разниц). Классы типов поддерживают мультипараметричность, и зависимости между агрументами. Классы типов поддерживают методы по умолчанию. Интерфейсы связаны с объектом (this), который их реализует и к которому применяется метод, для классов типов такой связи нет.

примерно так

qnikst ★★★★★
()
Ответ на: комментарий от qnikst

вывод инстанса по возвращаемому значению

Не понял. Вот что нашёл, читаю.

Классы типов поддерживают мультипараметричность, и зависимости между агрументами.

Тогда, если на языке С++, то туда ещё шаблоны навёрнуты. Не то? Зависимости между аргументами в плюсах тоже можно сделать за счёт шаблонов:

template <T> concatenate(List<T> a, List<T> b) {...}

den73 ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.