stdray: (Default)
Читал пост про книги по дизайну ООП приложений, и внезапно осознал, что прочитал большую часть приведенных книг.

И не могу сказать, что мне как-то это помогло. Скорее наоборот, после прочтения количество полезного кода резко сокращалось, а популяция пауков-розеточников росла. Ведь рефакторинги, иерархии интерфейсов, попытки работать с rich domain model, - все было контрпродуктивно. Поскольку задачи константой (известной) сложности внезапно приобретали новый порядок пиздеца человекочасов, где количество рефакторинга и доступных для задачи паттернов существенно превышало непосредственно бизнес-логику.

Конечно, это вопрос вопрос личного опыта и критического (его отсутствия) отношения, так как в определенный момент начитавшись ПФП, изучив какую-то литературу про F#, я решил, что можно невозбранно применять этот подход в повседневной работе. Как следствие - безумный, бездумный, ад пиздеца "фп стиль", который я потом рефакторил в куда более простые и идиоматичные конструкции, когда понял, что я таки буду менять работу. К сожалению, недорефакторил, и там _сложно_.

Я полагаю, что моя главная ошибка была в том, что всё это происходило хаотично, что вместо продумывания высокоуровнего дизайна приложения, я занимался дизайном уровня функции/модуля/компонента/библиотеки, при этом "слои" либо разово определялись на начальном этапе проектирования, либо формировались ( или не формировались) походу дела. Занятно, что "слои" в любой литературе носят уморительный характер, формализуемый, допустим, у Фаулера, мол уровень (level) - чисто интуитивная хуита, а вот слой (layer) - это можно разнести по разным машинам. Ну понятно, что первое и второе (до разноса) являются квазисущностями, которые держит в голове только породивший их на свет.

С этим бекграундом я с интересом прочитал статью, которая навязывает некоторый высокоуровневый дизайн в условиях убогого однопроходного компилятора F#. Занятно, что ограничения компилятора ведут к совершенно конкретному дизайну, к которому я приходил, занимаясь разработкой на эфсярпе. Но я никогда не смотрел на это в таком ключе, мол у вас просто и выбора нет. У вас есть единственно верный anemic подход, конпелятор, который иное не переварит и профит.

Понятно, что любому ограничению можно найти оправдвание (например системам типов), как можно и не делить приложение на слои. Мне просто понравилась трактовка убогости конпелятора, как некоторой статической валидации дизайна на высоком уровне.
stdray: (Default)
Вынесу из комментов, а то меня меня в противники ФП из-за отрицания киллер-фичности отступов записали недавно.


Субъективно, компактность достигается выбрасыванием лишних сущностей.

ООП - это про:
- делегирование ответственности и сертифицированные способы это сделать (патттерны)
- "безопастность уровня разработчка", ведь любой объект в терминах ООП - потенциальная точка расширения как добавлением узко-специфичной логики (костыль) и наследниками нарушающими инвариант, откуда идут закрытые классы (малая реиспользуемость) и избыточное количество деклараций (интерфейсы, полностью абстрактыне классы). Отсюда же пачка модификаторов и бойлерплейт.
- делегирование ответственности во вне: инъекция зависимостей через конфигурацию (xml spring ioc и ему подобные).
- сильную связанность (coupling), поскольку ООП-методы абстрагирования по спирали приводят к ней раз за разом, в виде того же жесткого каркаса на новом уровне абстракции. При этом растут затраты на отслеживаниее этих зависимостей.
- проблемы с тестированием (в силу предыдущего пункта), поскольку модульные тесты имитируют "архитектуру" системы. Формализм объектов - ДКА, и даже mock-объект должен имитировать его состояния.
- высокий порог вхождения. Несмотря на номинально низкий порог, разобраться в этом зверинце практически невозможно. Пока новички комплексуют от классов с пачкой статических методов, прошедшие этот этап налегают на тулинг и накапливают опыт.

ФП - это про:
- first class citizen функции
- безопастность для разработчика, через иммутабельность и чистоту функций
- решение конкретной задачи
- простое тестирование при отсутствие состояния
- высокий порог вхождения, несмотря на концептуальную простоту и доступность обучающих материалов, разбираться в этом зверинце никто не хочет. Пока неофиты пугаются ауры "научности", новички метаются между языками, книгами и прочим, прошедшие этот этап не имеют возможности применять знания технологии на практике.

ЗЫ: пишу про модульные тесты, потому что не понимаю, что такое юнит-тесты и не пишу их. А затраты на интеграционное тестировние (по моей практике) нивелируют разницу в подходах.

ЗЗЫ: ФП очень про анемичную модель (в терминах ООП). И, субъективно, такой подход проще (банально). Например, такая презентация.
stdray: (Default)
Часто слышу от своих знакомых, программирующих на C# и использующих ReSharper, что едва ли не самой полезной возможностью решарпера является проверка стиля кодирования. То есть решарпер проверяет правильность именования переменных, всякие там Pascal Casing для имен типов, Camel Casing для переменных и прочее такое. В этом есть смысл, поскольку многие программисты style guide не читают, а я читал, но почти ничего не помню. И тоже полагаюсь на ReSharper, хоть и регулярно возникают с ним разногласия.

А вот для Nemerle никакого решарпера нет, более того, нет никакого style guide. Размышляя над этим по дороге, я пришел к выводу, что второе существенно, а первое - не очень. Поскольку многое можно сделать на макросах. Например, захотел я удостовериться, что названия всех типов начинаются с большой буквы. Да еще чтоб я мог выбирать, что делать с несоответствием: выдавать ошибку компиляции, предупреждение или просто проинформировать. Несложно. Для этого надо лишь объявить макрос-атрибут уровня сборки

[MacroUsage(MacroPhase.BeforeInheritance, MacroTargets.Assembly)] \

macro CapitalTypeNames(alertType : string)

NameConventionImpl.CapitalTypeNames(Macros.Manager(), alertType)


А при его вызове, собрать типы, объявленные в сборке, отфильтровать имеющие некорректные названия и вывести информацию указанным образом. Read more... )
stdray: (Default)
Пока в сети продолжаются споры от том нужны DSL или не очень, я решил разобраться, как на макросах Nemerle реализуются встраиваемые языки. Потребность такая может возникнуть, даже при наличии макросов, расширяющих синтаксис языка. Дело в том, что менять правила лексера и препарсера нельзя, а значит определенные последовательности символов будут невалидными, например непарные скобки, хотя я подозреваю, что ими ограничения не исчерпываются. Для выхода из ситуации используются рекурсивные строки, внутри которых пишется код на интересующем языке, а потом транслируется в AST Nemerle. Подобным образом реализованы xml-литералы и linq query syntax.

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


Реализация МИКРОБЕЙСИКА )
stdray: (Default)
Декларативность – стиль оформления устных или письменных текстов, при котором заявленные цели и ценности не соответствуют реальным действиям и реальным возможностям автора этих текстов.

src
stdray: (Default)
Бывает, ради эксперимента пишу какие-то макросы, чтобы разобраться что позволяет макро-подсистема Nemerle, а что - нет. Если будет получаться что-то забавное, стану выкладывать это сюда.


Расширяемые анонимные типы

По мотивам хотелок [personal profile] metaclass. На сколько я понял, ему нужны анонимные типы, которые можно расширять, добавляя новые элементы, а также передавать за пределы той области видимости, в которой они были объявлены. В языке Nemerle нет анонимных типов, однако ими все пользуются, поскольку они реализованы через макросы.

def obj = new(name = "anon", age = 10, created = DateTime.Now);


В общий чертах, работает это так: макрос new по переданным параметрам генерирует генерик-класс с макро-атрибутом [Record] вида

[Record] class AnonType[name, age, created] {

public name : name;

public age : age;

public created : created;

}


Макрос [Record] создает конструктор с аргументами для всех полей, а потом вызов макроса переписывается в конструктор только что созданного класса.
После раскрытия макроса new
Read more... )
stdray: (Default)
В вики Nemerle существует вот такая страничка, на которой рассказывается, как могли бы вести себя трейты, будь они реализованы в языке. Но пока что "not yet done", хотя тема неоднократно поднималась на форуме. Как я полагаю, это все следствие размышлений на тему "Как бы нам вернуть обратно множественное наследование". То есть сейчас в дотнетах оно есть, но в значительно урезанном виде: интерфейсы позволяют наследовать только требования к объектам, а уж реализацию приходится пилить вручную, что конечно же раздражает.

Мне в этом описание понравилась идея выставлять требования для объектов, к которым возможно "подселять" трейты. Однако, мне не очень нравится идея реализовывать это дело внутри интерфейсов, поскольку такой объект будет некорректным с точки зрения, допустим, C#. К тому же такие трейты не смогут иметь собственное состояние, что очень сильно ограничивает варианты их использования.

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

Для описания примеси я использую модуль, помеченный атрибутом [DefMixin], внутри которого описывается интерфейс (он представляет собой требования) и класс (он представляет реализацию). Простейшее описание примеси может выглядеть так:

[DefMixin]

public module Foo {

public interface Require {

getX() : int;

setX(v: int) : void;

}

public class Provide[T] where T: Require {

public inc() : void { self.setX(self.getX() + 1); }

}

}


Узнать больше про примеси в Nemerle )
stdray: (Default)
Меня преследуют странные мысли о полиморфизме высших порядков (rank-2 и rank-n) и тайпклассах. Мне привиделось, что можназделоть что-то такое на макросах в Nemerle и теперь голова этим забита. То есть какой-то синтаксис, например poly

        def t1 = poly T[_];         // T1 [ T2 ] 
        def t2 = poly T[_  , _];   // T1 [ T2, T3 ] 
        def t3 = poly T[int, _];   // T1 [int, T2] 

по ентой записи делает представление . Примерно как дотнеты хранят как-то информация о generic-параметрах

        WriteLine(typeof(Dictionary.[_,_]));
       //System.Collections.Generic.Dictionary`2[TKey,TValue]
        WriteLine(typeof(Dictionary.[string,int]));
       //System.Collections.Generic.Dictionary`2[System.String,System.Int32]

Так и мне надо изобразить что-то такое. То есть сохранить информацию о типах-параметрах в какую-нибудь свой класс PolyType, а потом применять

        def с1 = t1 apply [List]; // List[T2]
        def r1 = с1 apply [int];  // List[int]

И у apply должен быть тип PolyType -> YobaType, где YobaType - это тип-вариант из PolyType (если у нас все еще слишком неконкретные для дотнета типы) и System.Type (когда тип стал конкретным, надо бы его прямо в код поставить).

И надо сделать такие вот тайпклассы

    typeclass Functor [ poly C[_] ] {
         abstract fmap[a,b](f: a->b, c: C[a]) : C[b];
    }
 

И все это дело будет превращаться в контейнер с квази-циатами. Будут хранится типы-параметры в виде YobaType, будут хранится сигнатуры методов, а также тела методов, если они не объявлены как abstract. И затем можна будет использовать все это дело каким-то макро аттрибутом вроде

    [ instance Functor[List] ]
    module ListF {    }
 

И тогда в ентот модуль ListF будут переписаны, все методы из Functor, сигнатуры будут конкретизированы List. Для методов помеченных abstract в базовом классе будет выдана ошибка с требованием реализовать такой-то метод. Методы, имеющие тела в базовом классе, будут переписаны в дочерний, если в том уже не содержится метод с данным именем и данной сигнатурой. Понавешать атрибутов [instance] на один модуль можно много, потому могут возникать конфликты. На самом деле, это не так серьезно, как может показаться, поскольку Nemerle умеет в перегрузку методов (в отличии от некоторых), потому конфликтовать будут только методы с одинаковой сигнатурой и именем, да и то если они они имеют реализацию в базовых классах. Ну и тут надо, как-то выкрутился, но я пока не понял как. Мне думает, это скорее вопрос соглашений, так как информации достаточно и реализовать-то можно всякое поведение.

И вот пытаюсь сделать что-то такое и до [profile] wizzard0 докопался и до чатика. Пока еще даже не определился, как должны PolyType и YobaType выглядеть. Также мучает вопрос "а не поехал ли я", но тут без разницы, если действительно можназделоть.
stdray: (Default)
Пришло время написать макрос. Макрос сам себя не напишет.

Я тут некоторое время писал кое-какую мелочевку на Nemerle, и, можно сказать, язык пришелся мне по нраву. Очень похоже на F# хотя и своими особенностями. У меня пару раз возникало желание написать макрос, который будет заниматься небольшой кодогенерацией по месту, но, после небольшого размышления, я отказывался от этой затеи. Отчасти из-за неприязни к особым формам и неявному поведению (странно, что при этом я обожаю всяческий синтаксический сахар), отчасти из-за сложности написания самих макросов. Чтобы выяснить, что же меня больше смущает, я решил научиться их писать. В этом посте, я старался систематизировать то, что узнал о синтаксических макросах Nemerle.

макросы такие макросы )

В один пост все не влезает. Остальное можно прочитать вот здесь.
stdray: (Default)
Пришло время написать макрос. Макрос сам себя не напишет.

Я тут некоторое время писал кое-какую мелочевку на Nemerle, и, можно сказать, язык пришелся мне по нраву. Очень похоже на F# хотя и своими особенностями. У меня пару раз возникало желание написать макрос, который будет заниматься небольшой кодогенерацией по месту, но, после небольшого размышления, я отказывался от этой затеи. Отчасти из-за неприязни к особым формам и неявному поведению (странно, что при этом я обожаю всяческий синтаксический сахар), отчасти из-за сложности написания самих макросов. Чтобы выяснить, что же меня больше смущает, я решил научиться их писать. В этом посте, я старался систематизировать то, что узнал о синтаксических макросах Nemerle.
макросы такие макросы )
stdray: (Default)


В этой шутке есть доля шутки, хотя непонятно какая именно. То есть я смотрел, как [personal profile] metaclass под всеобщие аплодисменты пытался какие-то низкоуровневые вещи на Haskell делать, которые потом, по-моему, в момент были переписаны на Python. Или же Scala, которая, по идее, попадает в правый верхний угол этой диаграммки, стремительным ростом своей популярности какбэ подтверждает корректность рассуждений. Хотя ведь и на Haskell бывают откровения, но тут сложно судить, что в итоге больше повлияло: мощь Хаскеля или же скил автора презентации опердени. Я думаю, что второе. Но тут как: чем чаще будет появляться информация об успешных проектах с использованием Haskell, тем выше мотивация для преодоления страха (лени?) перед всякими монадами, аппликативными функторами, полиморфизмом высших порядков и прочими отличными терминами. Чтобы что-то реальное писать на Haskell надо обладать неплохим скилом (как минимум уметь писать подобное на чем-то другом) и быть ко всему готовым. Scala же не заставляет сходу менять привычки, то есть можно по началу писать как на Java с маттерн-матчингом и замыканими, а потом втянуться. Ну а если с большого расстояния посмотреть, то суждение таково, что "все енто ихнее функциональное программирование - баловство, лучше прочитать новую книжку по паттернам, рефакторингу и последним тенденция в agile". А еще дальше оказывается, что большинство даже слышали про ФП и в особо запущенных случая запрещают LINQ использовать, ибо нипонятно.

С программированием что-то странное происходит. То есть куда-то уходит понимание, что основные инструменты программиста - это его мозг и язык программирования, вместо этого какая-то каша. Выясняется, что код компилируется вижаул студией, а не компилятором, что запросы к sql серверу надо делать исключительно мышкой. То есть появляется новый слой абстракции костылей. Развиваются инструменты для использования инструмента: IDE, рефакторинги, скалфолдинг, ГРАФИЧЕСКИЕ КОНСТРУКТОРЫ. И какие-то улучшения в этим компонентах вызывают гораздо больше восторга и обсуждение, чем в самом языке. Я вот смотрю, как выползает на свет .net 4.5, так программистам на додиезе плевать вообщем-то на async/await, как и на внезапное замыкание на значение счетчика цикла, но вот НОВЫЙ РЕВОЛЮЦИОННЫЙ СТИЛЬ СТУДИИ - гвоздь программы, то есть реально войны ведут лучше ли станет программироват с серыми иконками. Языков много, срачи какой лучше, олимпиады всякие... А победит (то есть получит наибольшее распространение) язык обладающий самым продвинутым инструментарием по работе с ним. И это точно будет не ФП язык, поскольку функциональщики они такие: "ide нинужно", и все. Но это так, одна из версий, хотя и весьма реальная, на мой взгляд. В принципе, декларативность достигается, хоть и такими корявыми методами, так что все ровно.
stdray: (Default)
Почему в F# и Nemerle списки, которые в коробке, являются энергичными, ведь с их помощью нельзя обрабатывать сколько-нибудь значительные объемы данных? В том же F# списки можно использовать только для написания симпатичных примерчиков вроде
  1. let rec insertions x = function  
  2.     | []             -> [[x]]  
  3.     | (y :: ys) as l -> (x::l)::(List.map (fun x -> y::x) (insertions x ys))  
  4.   
  5. let rec permutations = function  
  6.     | []      -> [ [] ]  
  7.     | x :: xs -> List.concat ( List.map (insertions x) (permutations xs) )  

но даже его, для реального использования необходимо переписывать через sequence. А sequence, в свою очередь, является простой оберткой на IEnumerable, для которого не определена определена операция Cons(head, tail), то есть в сопоставлении с образцом использовать нельзя, кроме как введением костылей вроде
  1. let (|SeqEmpty|SeqCons|) (xs: 'a seq) = //'  
  2.   if Seq.isEmpty xs then SeqEmpty  
  3.   else SeqCons(Seq.head xs, Seq.skip 1 xs)  

которые безбожно тормозят, поскольку каждый Skip порождает новую цепочку вычислений, которую надо разворачивать с самого начала. То есть матчить sequence нельзя, а для повторного использования результатов вычислений его постоянно оборачивают в Seq.cache. А на все вопросы относительно такого поведения, так называемые гуру дают ответ, что надо просто использовать LazyList из FSharpPowerPack. Ленивый список ок, его можно матчить и семантически он аналогичен кешированному Seq, но для него не определены computation expression, хотя это совсем незначительный эстетический недостаток.

В Nemerle существует некоторый хаос. То есть стандартизированных ленивых списков нет, зато существует огромное колличество подпорок, в которых можно затеряться

Так же, у меня сложилось мнение, что немерлисты односвязанному ииммутабельному списку предпочитают родные дотнетовские List[T] и oh shi~ Array[T] с соответствующими императивными паттернами, даже специальная конструкция для выхода из множества вложенных циклов есть. То есть ЧИСТОТА не в почете.

Есть какие-то разумные объяснения подобной ситуации? Почему они встраивают энергичные списки в язык, если ими физически невозможно пользоваться?


PS: Забыл сказать, что идет июньский конкурс по функциональному программированию. Мое текущее решение на Nemerle отрабатывает за 42 секунды на нетбуке. Хочу попробовать переписать код с использованием ассинхронных вычислений, надеюсь побыстрее будет. Может кто-то хочет показать класс, решив задачу еще быстрее?
stdray: (Default)
Сначала я хотел просто написать, что майский конкурс по ФП закончился, и желающие могут посмотреть, что мы там устроили. А если что-то из увиденного понравится, то и проголосовать.

Но потом я подумал, что это хороший повод рассказать о языке программирования Nemerle, с которым я игрался в последнее время. Опыт у меня с ним пока не большой, потому структурировано написать не выйдет. Под катом некторый набор сведений о Nemerle в перемешку с моим потоком сознания относительно функционального программирования и программирования вообще.


NEMERLE и поток сознания )

Update: Как-то получилось, что обсуждение велось параллельно в двух местах: тут и еще одна ветка обсуждения.
stdray: (Default)
А шабаши от программирования все еще проводятся. Пруфлинк.

Она какбы усмехается...
ВСЕМ ЗАВИДОВАТ

Я же, решил, что лишний перебор никому не помешает, ибо слова из словаря сами себя не переберут. В привычном стиле запостил код на F#, пытаясь поставить стандартный диагноз из разряда "Ничего не делаешь, а все считается". Это оказалось правдой лишь отчасти. Алгоритм вышел не особо оптимальный, потому обратная ОРОРО-задача "посчтитайте все варинаты" встала жирнющим таким вопросом. Да, F# дает возможность застрелить свою собственную ногу, написав кривое и медленное говно. Ну а кто не дает? Хачкель, призваный мешать программировать ради чистоты или типо того, тоже позволяет, но про него потом. Ну, написал медленный код. Ну, ок. Зато можно с простреленной ногой РЕЗКО ускорится, просто написав PSeq.blablablah вместо Seq.blablablah. Разница в одной букве, а дотнет все распаралелил в оптимальное количество потоков и стал считать в 4 раза быстрее. Можно уводить загрузку всех 4 ядер рабочего пека в потолок. Прелестно же.

Теперь про Хаскель. Если внимательно всмотреться в табличку, можно заметить, что Хаскель - язык для девочек мейнстримовый язык программирования, по крайней мере, в среде профессиональных участников специальных олимпиад. Таковых набралось 8 или 9 человек, а большего представительства не получил никто. Так оно и понятно, написав факториал на Хаскель, можно делать cool face "Я - повелитель матана", а в реальности это простой и понятный язык. И даже программировать на нем можно, хотя прямого pipe оператора и не хватает.

Я это к чему, вот ХАЧКЕ для девочек, а F# для рабочего и колхозницы. А какие есть риальне брутальные языки? Вот я сейчас закончу разбираться с УТРА-ЮНИТИ-ХАРДКОР ТИКУЛЬ, с tcl то есть, так за что мне такое взяться? Чтобы прямо разрывало от одного упоминания. Всплывают в мозгу всякие K, J, Unlambda, Clean и Omega. Есть идеи, чем упороться? Вещества не советовать, если че, я предпочитаю, чтобы УГАР входил прямо через глаза.

metaphone

Mar. 30th, 2012 07:51 pm
stdray: (pic#896193)

Наткнулся на хабрастатью, где рассказывается о фонетических алгоритмах. Штука достаточно распространенная, тот же soundex во всяких субд присутствует. Вещь скорее забавная чем полезная, хотя... Например, мною написана небольшая тулза, в которой ведется база наших контрагентов. Таки может и пригодится, поскольку эти чудики любят перерегистрироваться из "Рога и Копыта" в "Рога и Капыты", "Р'Ога и Копыта". В общем, забавы для написал русский алгоритм metaphone. Под катом код на F#, вдруг кому-то из пишущих под .NET понадобится.

row row fsharp the power )

Ну и да. F# с дотнетами отличные. Ничего не делаешь, а все работает.

December 2019

S M T W T F S
1234567
891011121314
15161718192021
222324252627 28
293031    

Syndicate

RSS Atom

Most Popular Tags

Style Credit

Expand Cut Tags

No cut tags