Как стать автором
Обновить

Комментарии 16

состояние (!) в чистом языке

Чистый не потому, что без состояния, а потому, что ссылочно-прозрачный.

"Функциональное программирование - это не то, что нам рассказывают"

https://habr.com/ru/post/479238/

Мне тоже такая формулировка не очень понравилась, если честно. Потому что достаточно открыть скажем википедию, и посмотреть, что такое чистая функция, и там написано:

В языках программирования чистая функция — это функция, которая:
является детерминированной;
не обладает побочными эффектами.


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

Хорошо, переформулирую

Итак, получили неидиоматичный код на функциональном языке и ворох проблем:

1) Скорость доступа к переменным О(n). Ок, можно улучшить, если подключить (unordered-)containers. Всё равно не то.

2) Переменные хранятся в гомогенной коллекции (переменные одного типа). Можно обернуть, но получим слабую типизацию и ошибки в рантайме.

3) Доступ к переменным по строкам (или любым другим идентификаторам), не определённые переменные вызывают ошибки.

А главное - зачем это всё?

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

Согласен с 1), но как разработчик в т. ч. на Python мне не столь критичны несколько лишних десятков миллисекунд, повторюсь этот проект в создан обучательных целях.

Со 2) думал, ничего лучше не придумал создавать тип как VarVal = IntVal Int | StrVal String ...

С 3) я разобрался используя типы Maybe, вот сигнатуры публичных get и del ф-ий


get :: Eq name => name -> VarState name val (Maybe val)
del :: Eq name => name -> VarState name val (Maybe ())

2) Почитайте про heterogeneous lists - например. Вкратце - можно использовать GADTs для сокрытия типа, и при желании сохранить какие-то из его инстансов - как минимум понадобится Typeable. Чтобы извлечь реальные данные придётся использовать Proxy типа и Refl - опять же всё некрасиво и теряем проверки на этапе компиляции, но произвольные данные хранить сможем.

3) Я имел в виду ошибки логики, а не доступа. В смысле, программист сделал опечатку, и дальше программа работает с Nothing, а не нашими данными.

Хорошо, спасибо, учту.

Значит, к примеру, State Integer () есть функция, преобразовывающая Integer в кортедж ((), Integer)

Непонятно. State -- это же конструктор типа, как он может быть функцией?
Что нужно написать в ghci, чтобы :t показало тип функции с указанной сигнатурой?

Извините, что вмешиваюсь.

Если я правильно понял именно ваш вопрос (в цитату из поста нужно дольше вчитываться), то так?

foo :: Integer -> ((), Integer)
foo age = ((), age)

GHCi 9.2.1

Спасибо. Но у меня вопрос больше про State, а у вас он не фигурирует. Можно как-то типизировать foo как State Integer () ?

И вот я в новом файле, не импортируя стандартный Control.Monad.State, написал код из статьи:
newtype State s a = State { runState :: s -> (a, s)}
instance Monad (State s) where
return x = State $ \s -> (x, s)
(State h) >>= f = State $ \s ->
let (a, newState) = h s
(State g) = f a
in g newState

При попытки компиляции получаю ошибку:
• No instance for (Applicative (State s))
arising from the superclasses of an instance declaration
• In the instance declaration for ‘Monad (State s)’
|
2 | instance Monad (State s) where
| ^^^^^^^^^^^^^^^
Failed, no modules loaded.

И вообще не понял эту строчку

instance Monad (State s) where

Разве здесь вместо параметра s не должен стоять конкретный тип?

Я имею ввиду, что обьект с типом State Integer () будет являться (обёрнутой) ф-ей типа Integer -> ((), Integer), спасибо за вопрос, уточню в статье

Спасибо.
Проходил летом курс на степике по Haskell Дениса Москвина (первая часть).
Застрял как раз на монадах. До State правда немного не дошёл.
На Reader почувстовал, что плыву. Надеялся с помощью вашей статьи сдвинуться с мёртвой точки.

Переформулирую вопрос выше.
Вот эта строчка "instance Monad (State s) where"
в ней s -- типовой параметр. Но s, что идёт далее несёт совсем другой смысл -- это параметр функций.
Так?
А совпадение имён чтобы кривую обучения задрать (шутка).

А когда типовой параметр может пригодиться в определении монады?
Ведь интерфейс уже задан в классе типов Monad.

Рад, что помог с пониманием :)

Использование типовых переменных в определениях инстанса встречается очень часто, чтобы частично применить тип, к примеру кайнд Maybe это * -> *, а необходимый для определения Монады это * -> *, и они совпадают, по этому мы просто определяем инстанс как instance Monad Maybe, а кайнд State есть * -> * -> *, по этому для определения инстанса мы его частично применяем. Про кайнды много рассказать не могу, но мы могу сказать что конкретный тип имеет кайнд *, а контейнеры это как бы функции к типам и имеют тип * -> *. Если будут ещё вопросы, добро пожаловать в личку.

Не "кортедж", а "кортеж". Это французское слово, никаких английских дж там нет.

Да сьтё ты гавались.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории