metaclass: (Default)
fp, cмерть мерзкая от монад, хаскель, штанга, гроб, гроб, кладбище
http://juick.com/2165512

Можно ли на всем этом сделать так: https://gist.github.com/4292029
И чтобы goodProcessingFunction можно было нормально комбинировать из отдельных кусков, вплоть до того, что явно определить точки, где данные юзера становятся данными для девайса и наоборот.
Или например, обощить это, прикрутив еще и какой-нибудь tcp сервер.

Т.е. у нас есть несколько независимых IO каналов, каждый со своими форматами данных, на каждый канал прикрутить сменный парсер-сериализатор, чтобы в ядро бизнес-логики этот канал приходил уже в виде значений нормальных типов.
А потом еще захочется хранить в бизнес-логике состояние, подключаться к БД и все это, по-моему, нахрен умрет под грузом типов и слоев монад.
metaclass: (дзедline)
http://juick.com/dmz/2147369#53
а мне вот, допустим, ваши реплики неприятны и потенциально вредят бизнесу. и если ко мне через месяц придет заказчик и скажет --- какого хуя вы вот мне софт для управления дц пишете на хаскелле, я тут читал автора ёрливидео, и он говорит, что хаскель --- говно, переписывайте всё нахуй на эрланге --- это будет уже прямой убыток. это маловероятно, но вполне может пойти в том ключе.
metaclass: (Default)
А вот, тащемта, можно ли и каким образом на хаскеле можно сделать обобщенную функцию такого вида:

На вход подается список функций разных типов, но общей структуры вроде такой:
f :: state -> input -> (new_state,output) (такая функция описывает блок обработки сигналов, с внутренним состоянием, типа фильтра).

На выходе наша обобщенная функция должна выдать нечто такое:
f_all :: state_all -> input_all -> (new_state_all,output_all), где state_all,input_all,output_all - объединение (кортеж) соответственно из всех состояний, входов и выходов переданных на вход функций. То бишь наша функция, из N блоков обработки сигналов делает 1 блок, входы которого - это входы всех составляющих блоков, выходы - выходы составляющих блоков и состояние аналогично.

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

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

PS: в общем, забавность ситуации в том, что строгая типизация помогла бы находить ошибки в блоках и соединениях, но с ней возникает необходимость использовать гетерогенные списки, SYB и прочую олеговщину. Причем я в это утыкаюсь регулярно - то с оперденями, то вот сейчас с обработкой сигналов.
metaclass: (Default)
Обошел баг с невозможностью дважды инициализировать/деинициализировать хаскелевый рунтайм в одном процессе - гружу либу с хаскелевым кодом динамически(LoadLibrary), работаю(hs_init,...,hs_exit), выгружаю(FreeLibrary).

Все бы хорошо, но не оставляет желание залезть во внутренности ghc и рунтайма и посмотреть, что же все таки такого они там сделали, что рунтайм нельзя повторно инициализировать после деинициализации.
Еще бесит, что gcc под виндой не умеет создавать отладочные файлы для windbg, к которому я привык, поэтому не могу толком глянуть на чем что падает.
metaclass: (Default)
Енумераторы-итератии наконец заработали так, как я себе это представлял.
Ошибок было две:
Ключевая: надо было IO action лифтить внутрь iteratee и там всю работу и выполнять. А не внутри IO дергать енумератор с итератее-парсером и доставая результат:

Read more... )
Вторая ошибка: в протоколе одного девайса после строки данных идет \r\n. в протоколе другого только \r. Строки разделены пустыми строками как бог на душу положит.
Я пишу парсер на attoparsec таким образом:
do skipWhile isSpace_w8 --пропускаем whitespace 
   data <- ReadData     --читаем строку данных
   skipWhile isSpace_w8 --пропускаем whitespace (т.е. - перенос строки)
   return data
Так вот, skipWhile, что характерно - будет ждать данных пока не придет хоть что-нибудь, отличающееся от предиката. Если девайс не гонит данные непрерывным потоком - то это повисает до истечения таймаута.
Таймауты, впрочем, вообще не обрабатываются - пакет serialport на них возвращает пустые строки. Дойдут руки - буду разбираться как это исправлять, кроссплатформенным образом.
И да, я вспомнил, зачем же я это все делал: меня задрало не иметь под руками прототипа софта, который можно единообразно запустить как винде, так и на линуксе и который бы работал с висящими на ком-порту девайсами согласно разнообразных от фонаря придуманных протоколов.
metaclass: (Default)
Три парсера : Read more... )

и их использование:Read more... )

Второй и третий работают. Первый глючит - пропускает куски потока. Потому как после разбора не сохраняет остаток буфера, прочитанного енумератором и не отдает его при продолжении, а начинает разбор уже со свежепрочитанного буфера.
А нужен именно первый, для того, чтобы посылать по одной команды на девайс и читать и парсить ответы. Т.е. один цикл работы связки enumerator $$ iteratee на каждый ответ от девайса.
Либо использовать не enumerator, а более сложную реализацию - пакет iteratee, который умеет из Iteratee посылать команды обратно в Enumerator, используя исключения и их перехват.
Либо забить и сделать в лоб, т.к. тут на самом деле Iteratees нужны только для сушения мозга и удобства комбинирования парсеров и прочих фильтров потоков.
metaclass: (Default)
Проверяю, действительно ли enumerator+attoparsec работают в постоянной памяти.
Собираю с профилировочной информацией:
ghc.exe -odir obj -hidir obj --make "%1" -prof -auto-all -caf-all -fforce-recomp -rtsopts -fext-core
(если чо - в кабале перед установкой пакетов надо в конфиг поставить опцию library-profiling: True иначе будет ругаться при сборке)

затем запускаю прожку с профилировщиком
test.exe +RTS -p list2.txt >rr

list2.txt - файлик в 18 мег, 316 тыщ строк, список файлов на диске С :)

получаю файл test.prof, в котором есть такая забавная информация:
COST CENTRE                    MODULE               %time %alloc
main                           Main                  48.6    8.7
notNewLine                     Parsers               36.0   73.4
parseLine                      Parsers               10.6   15.5


notNewLine, который так интенсивно долбится в память - совершенно тупой предикат:
notNewLine :: Word8 -> Bool
notNewLine 10 = False -- LF
notNewLine 13 = False -- CR
notNewLine _  = True


Решил глянуть, что же ghc генерит для него:
Read more... )

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

PS: Ага, включил -O2 - предикат стал работать как положено - 2% времени и 0% памяти. И общее потребление памяти вообще упало в 5 раз.
metaclass: (Default)
iterParseDeviceMsg :: Monad m => Enumeratee ByteString DeviceMsg m b
iterParseDeviceMsg = sequence $ iterParser $ parseDeviceMsg

читает последовательность строк из источника, выдает на выходе последовательность DeviceMsg.
sequence взят из Data.Enumerator, где написано:
-- | Feeds outer input elements into the provided iteratee until it yields
-- an inner input, passes that to the inner iteratee, and then loops.
sequence :: Monad m => Iteratee ao m ai
         -> Enumeratee ao ai m b

Что характерно, я не очень понимаю, что я делаю, кроме того, что превратил Iteratee(сток для данных) в Enumeratee(фильтр-конвертор для потока данных).
В итоге использование выглядит таким образом:
processWeather1:: MonadIO m => SerialPort -> m ()
processWeather1 serial = do
     E.run_ $ enumSerial serial E.$= iterParseDeviceMsg E.$$ printObjI   


Идея примерно в следующем: данные читаются из потока небольшими кусками и скармливаются парсеру, который выдает на выходе последовательность уже разобранных данных. И предположительно, это все должно работать в постоянной памяти.
Можно было бы сделать вообще влоб, но с Iteratees более мозгозасушивающе, заодно можно копипастить идеи из http://www.yesodweb.com

Сейчас вот еще к этой хрени нужно прикрутить rrdtool для складывания считанных значений в него и какой-нибудь сервис попроще, чтобы отдавать текущие показания датчиков.
metaclass: (Default)
В Iteratees обратная связь от iteratee к enumerator реализована хитрым образом - через исключения. Т.е. кидается исключение с данными, которые нужно обработать в источнике данных, например, указатель для рандомного seek в файле, оно перехватывается енумератором-источником данных и обрабатывается.

Мне видимо, придется посылать команды ком-порту в том же стиле, но вообще надо еще посушить мозги над структурой типов используемых в Iteratees, чтобы понять, почему сделано именно так, а не иначе.
Ну и посмотреть, как пакет enumerator используется в Yesod, на предмет того же - как они читают из сокета и пишут в него ответ.
metaclass: (Default)
Пишу на хаскеле прогу, которая использует пакет ConfigFile.
Оный пакет в результате парсинга файла конфига выдает хитрожопую комбинацию монад ErrorT CPError IO ConfigParser
В итоге все это вырождается в кошмарные конструкции типа http://hpaste.org/52572
Вот у людей такая же проблема http://stackoverflow.com/questions/2216164/haskells-liftios-litter-functions-of-type-errort-string-io

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

Profile

metaclass: (Default)
metaclass

April 2017

S M T W T F S
      1
2345678
9101112 131415
16171819202122
23242526272829
30      

Syndicate

RSS Atom

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags
Page generated Sep. 24th, 2017 05:27 pm
Powered by Dreamwidth Studios