169 похожих чатов

Расскажу я вам сейчас одну офигенную историю, что со мной

произошла.

Итак, коллега решал для себя простенькую задачу по реализации интерпретатора бреинфак-подобного языка на js и я решил, что чего бы мне тоже не написать, на хаскеле естественно.

Сразу была написана фримонада для AST и интерпретатор. Команды всего четыре, инкремент значения в памяти, декремент, установка в нуль и вывод. Поэтому для интерпретатора я заюзал StateT для памяти и WriterT для вывода. Думаю, код понятен, если работали с free (если нет, пишите, я все с радостью объясню в любое время суток).

runBrainscrambler :: Brainscrambler a -> String
runBrainscrambler
= flip execWriterT ""
. flip evalStateT 0
. foldFree go
where
go (Increment next) = modify succ >> pure next
go (Decrement next) = modify pred >> pure next
go (PushZero next) = put 0 >> pure next
go (Output next) = get >>= tell . show >> pure next

ВАЖНО ЗАМЕТИТЬ: код полностью рабочий, он не просто компилируется, он выдаст правильный результата на любых примерах.

Но тут есть несколько ошибок, которые я не заметил, так как не написал тайпинги, мол хаскель сам выведет. Кто их уже заметил, молодец, остальным тоже советую посидеть и поискать.

В проекте было подключено OverloadedString-расширение, так что любой строковой литерал полиморфен и компилятор будет кидать ворнинг о том, что "используется дефолтный тип, так как тип не указан явно", если тип не указать явно. Собственно в коде выше "" не аннотирован явно, но тип четко выводится из результата функции (String). Ворнинг тем не менее был:

• Defaulting the following constraint to type ‘[Char]’
IsString b0 arising from the literal ‘""’
• In the second argument of ‘flip’, namely ‘""’
In the first argument of ‘(.)’, namely ‘flip execWriterT ""’
In the expression:
flip execWriterT "" . flip evalStateT (0 :: Int) . foldFree go
|
16 | = flip execWriterT ""
| ^^

Для меня это стала загадкой, с просьбой решить ее я обратился в @haskellru, но там никто не смог помочь (говорили про то, что ворнинг из-за того, что нет аннотации, но не увидили, что тип четко известен, полагаю никто, как я, в чужой код не вчитывается).

И вот я решил добавить тайпинги и весь код сломался:

go :: BrainscramblerF a -> StateT Int (Writer String) a

И вот тут-то я наконец понял, в чем дело.
1. У меня Writer, а не WriterT, поэтому нужно execWriter, а не execWriterT
2. (exec|eval|run)WriterT? не принимают начального "состояния", так как они работают с моноидом и берут в качестве начального вывода mempty.

Эти две ошибки вместе дают ОЧЕНЬ интересный результат. Раз execWriterT не принимает начального состояния, то почему выше код работал? Что именно flip флипал? И тут до меня дошло озарение я в спешке побежал проверять:

:t flip execWriterT
flip execWriterT :: b -> WriterT c ((->) b) a -> c

Именно, из-за flip хаскель вывел инстанс монады для (->) b, то есть Reader. И использовал этот (->) b как монаду для дыры монадного трансформера WriterT. Тот "" был не начальным состоянием WriterT, а энвароментом Reader, который в коде нигде явно не используется (нет ask), поэтому и тип вывести не получается, поэтому и был ворнинг.

Это эпичнейшая ошибка в моей жизни, скажу честно. Итоговый правильный код выглядит так:

runBrainscrambler :: Brainscrambler a -> String
runBrainscrambler
= execWriter
. flip evalStateT (0 :: Int)
. foldFree go
where
go :: BrainscramblerF a -> StateT Int (Writer String) a
go (Increment next) = modify succ >> pure next
go (Decrement next) = modify pred >> pure next
go (PushZero next) = put 0 >> pure next
go (Output next) = get >>= tell . show >> pure next

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

1 ответов

1 просмотр

Пиши на ruHaskell мини-статью

Похожие вопросы

Обсуждают сегодня

Anyone here suffers from unexplained aural migraines, who would be up for talking for a bit? Doesn't *have* to be aural, but I am not asking about headaches, I mean actual mi...
Martin Rys
55
Добрый день! Хочу спросить совета. Хочется в скрипт добавить некую конструкцию, что скрипт создал файл, который нужно потом скопировать в определенное место. Нашел такой сп...
Mikhail
3
Вопрос по WIN32: Насколько я понимаю то все функции win32 привязаны к объектам операционной системы. Например консоль, файл, кисть, окна итд. Следовательно функции win32 упра...
Tommy Vercetti
6
подскажите пожалуйста, как мне освободить результат записанный в переменную result? в чем проблема подскажите если МОЖЕТЕ?
Михаил Helper
28
кто-то пользуется компонентами rx ? как их лучше ставить, через OPM? (lazarus)
Iluha Companets
15
Hello, can I install macOS and run flutter on a virtual machine? Is it possible to print for iphone?
Mazones
11
я не магистр хаскеля, но разве не может лейзи тип конвертнуться в не-лейзи запросив вычисление содержимого прям при инициализации?
deadgnom32 λ madao
100
есть тут кто-то , кто только начал изучать си? если проходите курс на степике или как-то сами изучаете, пишите, может, скооперируемся?..
Eule
25
Дороу! У кого-нибудь есть в загашнике проверенная функция экранирования, аналог re.escape в Python? Из доки: » Escape all the characters in pattern except ASCII letters and n...
Daniil Smolyakov
9
В смысле более затратная? Общая стоимость владения лошадью меньше, чем автомобиля. В среднем.
Sergej R
10
Карта сайта