Как раз не нужно. В типе должна быть семантика, а не мусор.
Ничего не упало.
ф-я вернула nothing = вернула ошибочный код возврата = бросила исключение. Ну называть это упала или нет - выбор каждого уже.
Только в последнем случае можно променять Either на исключение
Так больше нигде и не надо. Будет просто «Config -> ..., BackendConnection -> ..., LocalCache RemoteCache -> ..., это же самое внутри структур в data, ... -> (Int Double), ... -> ParserResult ParserError». Без мусорных Either/Maybe.
ни где-то ещё — return codes и exceptions это две разные вещи
Ну да, return codes - устарелое неюзабельное говно, а исключения - система специально разработанная ему на замену.
другое — может работать нелокально через весь стек вызовов разных функций.
Смотри, если ты применяешь функцию - значит ты знаешь, что она делает (то есть уже посмотрел документацию). А если ты знаешь, что она делает, то знаешь, чистая она или нет.
Там вообще то речь шла именно о том, как _человеку_ отличать. Про компилятор - это уже ты придумал. Так что ИРЛ аннотации не нужны, чистая функция или нет - известно заведомо. Что до оптимизаций - тут уже правила совершенно другие и разговор совершенно другой. В 99.9% задач оно не нужно.
Что-то я не понял — чистый код вычисляется (пусть параллельно и нестрого) в какой-то _|_ (там где-то undefined, error, throw и т.п. — всё исключения), либо сложная структура содержит такой код — почему нельзя в чистом коде сделать let result = something in if isEvaluatesToBottom result then somethingElse else result с чистым же значением (result который _точно_ не какой-то _|_, либо somethingElse, ясно, что isEvaluatesToBottom не может просто протащить санк нестрогого вычисления — он должен сказать точно _|_ это или нет (и не содержит ли строгая структура его, поэтому я там поставил !, !! и NFData), но ссылочной прозрачности эта возможность не отменяет)? Мне, вообщем-то, всё равно как оно может быть реализовано, но, да, это явно более специального вида исключения нежели исключения в IO у GHC (без маскировок, асинхронности и т.п.).
и в хаскеле тоже lookup из разных контейнеров и окружения (POSIX, в смысле) возвращает Maybe.
Насчёт
Семантика-то одна в итоге.
Это как-то глупо — разве что в очень далёком и абстрактном итоге, обычно семантика исключений требует дополнительного к базовой семантике (функции, возвращаемые значения) построения (как в TAPL, 14).
Так больше нигде и не надо
Ээ, я хочу передать Nothing либо Just backendConnection — в одном случае будет одна работа, в другом — другая. Аналогично с передачей Left localCache и Right remoteCache — совсем разный код по двум ветвям. Типо DI == передача через аргумент.
LocalCache RemoteCache
(Int Double)
Что это?
Ну да, return codes - устарелое неюзабельное говно
Передавать и возвращать вариантные ADT не нужно, вообще ничего передавать и возвращать не нужно — всё можно протащить через исключения, hands down!1
Ты же говоришь, что «некорректная программа в языке со статической типизацией не скомпилируется». Однако компилируется, падает, предупреждений при компиляции не даёт.
Речь, наверно, про некорректные с точки зрения несовпадения типов программы. То о чём ты говоришь не имеет отношения к вопросу «статическая vs динамическая типизация» — это вопрос о тотальных и частичных (предполагающих исключительные ситуации) функциях. В хаскеле такие же частичные функции как и во всех остальных распространённых ЯП и исключения ловятся так же с помощью try/catch как и в остальных языках с исключениями. То что статическая система типов избавит от всех некорректностей вообще никто не говорит.
ага, ещё есть проблема с тем, что _|_ есть только у boxed values, в то время, как для оптимизаций всё что можно желательно сделать unboxed, и большая часть будет сделано компилятором, а подобный подход отменит такую возможность, плюс не будет содержать плюсов по сравнению с pattern-matching-ом по ADT и их монадическому интерфейсу, если он есть..
некорректные с точки зрения несовпадения типов программы
Тогда толку от них? Что-то более-менее сложное без костылей не сделать (потому как система типов не даёт ни типов объединений, ни функций с бесконечным числом однотипных аргументов).
Уж лучше Typed Racket, где что можно проверится на этапе компиляции, а всё остальное при первом же тестовом прогоне.
К слову, в нём нормально проверяется:
(define (test)
(let ([s (read)])
(+ s 1)))
Type Checker: Expected Number, but got Any in: s
а вот фиг без задачи поймешь, что он там хочет, может экзистенциальные, может хватит и typeable, может dynamic хочет, а может тегирование типов, может хватит врапа в ADT, может ему SumTypes на TypeOperators нужно, а может Typed protocol, когда тип разбираемого выражения или ответа основывается на полученном типе. Все нужно и полезно для своих задач..
(: fold-left
(All (C A B ...)
((C A B ... B -> C) C (Listof A) (Listof B) ... B
->
C)))
(define (fold-left f i as . bss)
(if (or (null? as)
(ormap null? bss))
i
(apply fold-left
f
(apply f i (car as) (map car bss))
(cdr as)
(map cdr bss))))
или привычке руками проставлять везде тип «на всякий случай». У тебя в твоей функции Integer 4 раза повторяется. Понадобится сделать вместо него Number или Double, будешь везде руками переписывать (или IDE с рефакторингом для Хаскеля уже сделали?).
извини, но если ты хочешь ругать язык, то будь добр ознакомиться с теорией. мне нечего ответить на твой пост, кроме как повторить мое предложение, прочитать что такое вывод по контексту и как он работает. Прочитав и поняв это ты узнаешьпочему я не буду проставлять тип «на всякий случай», и почему я у меня не будет проблемы с прописыванием везде другого типа. Может быть ты сможешь разобраться с приведенным мной кодом, тогда ты поймешь, почему мне нужно было прописывать тип явно и какие у него проблемы, которых ты не заметил.
Hint: почему я не прописывал тип тут.
*Main> add 1 2 3 4 + 6
16
P.S. ну и не забудь, что данная задача возникла только из желания показать твоё незнание языка, не более того, в реальной жизни таких задачь нет.
Речь завели о том, что «Чистые функции оптимизируются и распараллеливаются очень легко.» Привожу дословно.
Ну, что же. Много ли языков, где чистота гарантируется на уровне языка? И много ли среди них языков неленивых, т.е. с энергичной моделью вычислений? По правда говоря, среди популярных языков не знаю ни одного такого. Зато немного знаком с хаскелем, который гарантирует чистоту, и который является ленивым.
Значит, предполагаю, что имелся в виду все же хаскель. Тогда вспомним, что в Real World Haskell есть целая глава, посвященная распараллеливанию. И что, у вас не создалось ощущения чего-то неправильного после ее прочтения? Сколько там надо огорода городить чтобы распараллелить! Это раз.
Во-вторых, по умолчанию много-поточный рантайм хаскеля почему-то отключен в ghc (отключен -threaded). Значит, не все там так благополучно. И это можно понять.
Может быть, дело в санках (thunks)? Мало того, что они непредсказуемы, а потому не всегда ясно, что и где будет исполняться в каком потоке, так эти санки еще требуют некоторой блокировки при первом обращении к санку в много-поточном рантайме (или в Microsoft Research сидят такие кудесники?). Возьмем для примера реализацию Lazy из .NET. Блокировка там присутствует. Ну, а куда без нее?
Нет, конечно, если рантайм немногопоточный, то тогда блокировки для санков не нужны, но мы же говорили о том, что «Чистые функции оптимизируются и распараллеливаются очень легко.»
Тем не менее, не оставляю надежды, что люди из Microsoft Research в тысячи раз умнее меня, и что-нибудь придумают эдакое.
Значит, предполагаю, что имелся в виду все же хаскель. Тогда вспомним, что в Real World Haskell есть целая глава, посвященная распараллеливанию. И что, у вас не создалось ощущения чего-то неправильного после ее прочтения? Сколько там надо огорода городить чтобы распараллелить! Это раз.
RWH немного устарел, нужно читать черновики будющей книги Семёна Марлоу, сейчас все проще.
Дело в санках (thunks). Мало того, что они непредсказуемы, а потому не всегда ясно, что и где будет исполняться в каком потоке, так эти санки еще требуют некоторой блокировку при первом обращении к санку в много-поточном рантайме (или в Microsoft Research сидят такие кудесники?). Возьмем для примера реализацию Lazy из .NET. Блокировка там присутствует. Ну, а куда без нее?
блокировки нет слава ленивости, используется механизм (gray holes) позволяющий обойтись без блокировок, но возможно приводящий к повторным вычислением, с механизом обнаружения повторных вычислений, если интересно могу ссылки на статьи SPJ дать, но не сегодня.
Хотя учитывая то, что далеко не все, что SPJ понаписал принято в апстрим, возможно щас и не так, но успешные исследования есть.
Значит, предполагаю, что имелся в виду все же хаскель.
Я имел в виду только то, что сказал: что если есть гарантия чистоты, вычисления можно легко распараллелить или применить какие-нибудь сильные оптимизации, изменяющие порядок вычислений.
Никакой конкретный язык не предполагался, у вас СПГС.
Я имел в виду только то, что сказал: что если есть гарантия чистоты, вычисления можно легко распараллелить или применить какие-нибудь сильные оптимизации, изменяющие порядок вычислений.
Никакой конкретный язык не предполагался, у вас СПГС.
Ну, я так и думал.
Проблема в том, что если язык гарантирует чистоту, то ему уже желательна ленивость (не охота распинаться на эту тему).
Так, мне надоело.. расскажи мне real-world задачу, в которой нужно или удобнее использовать многопараметричность, во всех языках подобная возможность это сахар над передачей списка. Как понимаешь без многопараметричности данная задача не тривиальна.
Проблема в том, что если язык гарантирует чистоту, то ему уже желательна ленивость (не охота распинаться на эту тему).
слишком сложный вопрос, например читая хотя бы Окасаки увидеть, что ленивость необходима, для многих задач, так же это является очень сильным средством оптимизации и необходима для многих алгоритмов. Применять ли ленивость по умолчанию, вопрос хороший, последний тренд, с которым в среднем все соглашаются это: нужна ленивость в функциях и строгость в полях структур (до тех пор пока не доказана необходимость в обратного, а это бывает нужно).
завтра пни, мне сегодня ещё лекцию дописать надо.
I
что-то вроде forall a . (Type a) => (a -> a) -> Int -> [[a]] -> [[a]]
там в зависимости от того что именно тебе нужно, но мне сложно воспринимать просто игры с типами, проще хотя бы со спецификацией программы, а то наше общкние выродится в решение A B problem.