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 ответов

16 просмотров

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

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

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

Господа, а что сейчас вообще с рынком труда на делфи происходит? Какова ситуация?
Rꙮman Yankꙮvsky
29
А вообще, что может смущать в самой Julia - бы сказал, что нет единого стандартного подхода по многим моментам, поэтому многое выглядит как "хаки" и произвол. Короче говоря, с...
Viktor G.
2
30500 за редактор? )
Владимир
47
а через ESC-код ?
Alexey Kulakov
29
Чёт не понял, я ж правильной функцией воспользовался чтобы вывести отладочную информацию? но что-то она не ловится
notme
18
У меня есть функция где происходит это: write_bit(buffer, 1); write_bit(buffer, 0); write_bit(buffer, 1); write_bit(buffer, 1); write_bit(buffer, 1); w...
~
14
Добрый день! Скажите пожалуйста, а какие программы вы бы рекомендовали написать для того, чтобы научиться управлять памятью? Можно написать динамический массив, можно связный ...
Филипп
7
Недавно Google Project Zero нашёл багу в SQLite с помощью LLM, о чём достаточно было шумно в определённых интернетах, которые сопровождались рассказами, что скоро всех "ибешни...
Alex Sherbakov
5
Ребят в СИ можно реализовать ООП?
Николай
33
https://github.com/erlang/otp/blob/OTP-27.1/lib/kernel/src/logger_h_common.erl#L174 https://github.com/erlang/otp/blob/OTP-27.1/lib/kernel/src/logger_olp.erl#L76 15 лет назад...
Maksim Lapshin
20
Карта сайта