проектирование
От: neFormal Россия  
Дата: 30.06.16 15:52
Оценка:
А есть информация/ссылки/етк по поводу особенностей проектирования с разными фп-языками?
Поделитесь! Интересно почитать, сравнить.
...coding for chaos...
Re: проектирование
От: Nick Linker Россия lj://_lcr_
Дата: 24.06.17 16:06
Оценка: 60 (2)
neFormal,

F>А есть информация/ссылки/етк по поводу особенностей проектирования с разными фп-языками?

F>Поделитесь! Интересно почитать, сравнить.

Давненько не заходил сюда, отвечаю с большим лагом.
Современная архитектура в ФП: http://degoes.net/articles/modern-fp
Современное ФП, часть вторая: http://degoes.net/articles/modern-fp-part-2
Моки не нужны: http://rea.tech/to-kill-a-mockingtest/
Крутая идея, свободные монады: https://michaelxavier.net/posts/2014-04-27-Cool-Idea-Free-Monads-for-Testing-Redis-Calls.html
Про free, freer monads и эффекты как тип можно прочитать у Олега Киселёва (того самого)

Идея действительна настолько крутая, что зашла даже питонистам:
https://www.youtube.com/watch?v=D37dc9EoFus
Хотя выглядит в питоне это довольно отвратно https://github.com/python-effect/effect
(Не говоря уже о проверке компилятором).
Огромный пласт занимает FRP, и в джаваскриптах прямо революция. Особенно мне понравилась выступление Стальца, презенташка тут: https://speakerdeck.com/staltz/what-if-the-user-was-a-function

Как правильно работать с ошибками и исключениями в хаскеле:
https://www.fpcomplete.com/blog/2016/11/exceptions-best-practices-haskell
(Особенно 2 последних примера)

Вообще в двух словах не расскажешь, так что если есть что-то более специфическое спросить, то лучше так.
quicksort =: (($:@(<#[),(=#[),$:@(>#[)) ({~ ?@#)) ^: (1<#)
Re[2]: проектирование
От: neFormal Россия  
Дата: 24.06.17 19:02
Оценка:
Здравствуйте, Nick Linker, Вы писали:

спасибо за ссылки!

NL>Моки не нужны: http://rea.tech/to-kill-a-mockingtest/


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

NL>Вообще в двух словах не расскажешь, так что если есть что-то более специфическое спросить, то лучше так.


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

спрошу про наболевшее в haskell: как сделать наследование или заменить его?
есть у меня, к примеру, Paratrooper
делаю я пушку, которая умеет рисоваться, стрелять и принимать команды управления. пушка, вертолёты и пульки хранятся в одном списке, чтобы их можно было между собой сортировать и отрисовывать.
в какой-то момент хочется сделать пушку, у которой два ствола, и стреляет она другими пульками. а потом пушку с двумя стволами и ракетницей, и стреляет это всё третьим видом пулек и ракетами.
в ООП это всё делается через переопределение методов отрисовки и обновления состояния. при этом через шаблонный метод у меня всё это доступно весьма прозрачно. и при этом я ещё могу обратиться к родительской реализации и не дублировать код. например, вызвать отрисовку первой пушки, а второй ствол нарисовать рядом в дочернем методе.
в хаскеле мне придётся каждый нужный мне метод реализовывать отдельно. и это приводит к огромному оверхеду.
что с этим можно сделать?
...coding for chaos...
Re[3]: проектирование
От: dmz Россия  
Дата: 20.07.17 09:00
Оценка:
F>в хаскеле мне придётся каждый нужный мне метод реализовывать отдельно. и это приводит к огромному оверхеду.
F>что с этим можно сделать?

Использовать тайпклассы и композицию
Re[4]: проектирование
От: neFormal Россия  
Дата: 20.07.17 09:12
Оценка:
Здравствуйте, dmz, Вы писали:

dmz>Использовать тайпклассы и композицию


каждую ф-цию тайпкласса придётся переопределять в дочерних. это я уже пробовал.
ещё предлагали хранить ссылки на ф-ции и получить эдакую динамику, как в питоне. или сделать свою vtable.
...coding for chaos...
Re[4]: проектирование
От: dmz Россия  
Дата: 20.07.17 09:16
Оценка:
Здравствуйте, dmz, Вы писали:


F>>в хаскеле мне придётся каждый нужный мне метод реализовывать отдельно. и это приводит к огромному оверхеду.

F>>что с этим можно сделать?

dmz>Использовать тайпклассы и композицию.


Но IRL, конечно же, выяснится что никакую пушку из двух пушек никто никогда не композирует для
рисования, и что спрайты с фазами или векторная модель отдельно — и для неё вообще неважно,
двухствольная это пушка или гатлинг, активные точки, которые как-то взаимодействуют — отдельно
и их изначально N штук, ракеты отдельно и так далее.

А все эти истории про наследование двухствольной пушки от одноствольной, кенгуру с ПЗРК,
наследование квадрата от прямоугольника или прямоугольника от квадрата — не более, чем
народные анекдоты и сказки дедушки Гради Буча.
Re[5]: проектирование
От: dmz Россия  
Дата: 20.07.17 09:19
Оценка:
dmz>>Использовать тайпклассы и композицию

F>каждую ф-цию тайпкласса придётся переопределять в дочерних. это я уже пробовал.

F>ещё предлагали хранить ссылки на ф-ции и получить эдакую динамику, как в питоне. или сделать свою vtable.

Ну пытаться всё делать привычными инструментами в любом языке можно, но неконструктивно.
Так-то --- полиморфизм — есть, модульность --- есть. Проблем с декомпозицией задачи обычно не бывает,
а как сделать что-то конкретное --- надо смотреть на конкретный пример. История про пушку выглядит
надуманной.
Re[6]: проектирование
От: neFormal Россия  
Дата: 20.07.17 09:35
Оценка:
Здравствуйте, dmz, Вы писали:

dmz>История про пушку выглядит надуманной.


вот сейчас было обидно ._.

это действительно так происходит.
три компонента: отрисовка, управление и разные порождаемые объекты, — вот из чего складываются все эти комбинации.
обычно меняется 2 из 3х.
...coding for chaos...
Re[7]: проектирование
От: dmz Россия  
Дата: 20.07.17 13:22
Оценка:
dmz>>История про пушку выглядит надуманной.

F>вот сейчас было обидно ._.



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

Просто постановка до боли напомнила замшелые задачки для собеседований — типа кто от
кого наследует — квадрат от прямоугольника или наоборот. Или построить иерархию
наследования для геометрических фигур.


Определение похожего поведения для разных типов обычно решается
тайпклассами или явной диспетчеризацией (PM) или совмещением
обоих подходов.

Может, лучше какие-то кейсы привести? Так-то:

class Drawable a where
   draw :: Canvas -> a -> Canvas

data GunOneBarrel = GunOneBarrel GunAttributes

instance Drawable GunOneBarrel where
  draw _ (GunOneBarrel{..}) = undefined

data GunTwoBarrels = GunTwoBarrels GunAttributes

instance Drawable GunTwoBarrels where
  draw _ (GunTwoBarrels{..}) = undefined
Отредактировано 20.07.2017 13:27 dmz . Предыдущая версия . Еще …
Отредактировано 20.07.2017 13:27 dmz . Предыдущая версия .
Отредактировано 20.07.2017 13:26 dmz . Предыдущая версия .
Re[8]: проектирование
От: neFormal Россия  
Дата: 20.07.17 14:19
Оценка:
Здравствуйте, dmz, Вы писали:

dmz>Нет, ну правда — отрисовка двухствольной пушки как-то отличается

dmz>от одноствольной? Казалось бы, и то, и другое — либо спрайт, либо
dmz>векторная модель в том или ином виде.

одноствольная состоит из двух спрайтов(башни и ствола), а двуствольная из трёх.
при этом при наследовании можно вызвать родительский метод отрисовки и к нему добавить второй ствол.
в случае х-я родителя так не вызвать, придётся дублировать код.
можно завести себе поле "parent" и обращаться к нему, но в случае нескольких уровней потомков нельзя вызвать отрисовку, обратившись прозрачно "насквозь" через несколько родителей.
вот вызов методов далёких предков я и ищу.

dmz>Может, лучше какие-то кейсы привести? Так-то:


да, надо достать пример. записан был.
...coding for chaos...
Re[9]: проектирование
От: dmz Россия  
Дата: 20.07.17 15:57
Оценка:
.

F>одноствольная состоит из двух спрайтов(башни и ствола), а двуствольная из трёх.

F>при этом при наследовании можно вызвать родительский метод отрисовки и к нему добавить второй ствол.
F>в случае х-я родителя так не вызвать, придётся дублировать код.

В случае хаскеля нет никакого "родителя". Если у нас стоит задача отрисовывать
конструкции, составленные из нескольких спрайтов, то можно этого достичь примерно
9000 разными способами.

Один из них, например — взять значение, и выдернуть из него все спрайты
которые и отрисовать скопом:

let sprites = [x | x@(Sprite{..}) <- universeBi myMegaWtfObject]
mapM_ (drawSprite ctx) sprites



F>можно завести себе поле "parent" и обращаться к нему, но в случае нескольких уровней потомков нельзя вызвать отрисовку, обратившись прозрачно "насквозь" через несколько родителей.


F>вот вызов методов далёких предков я и ищу.


Алё, тут нету наследования. Какие еще предки. Это же хаскел. И заметим, много где нету,
но ничего, живут как-то. Алсо глубокие наследования вроде бы ```considered harmful```,
и как свидетель системы с глубокими цепочками наследования --- соглашусь.


Ну и вообще --- мне тут видится подвох. С каких это пор графические объекты в играх
композируют в коде из спрайтов? А не рисуют/моделируют/размечают в редакторе? Скорее
всего, IRL тут будет таки один спрайт, смоделированный в редакторе, и какая-то
метаинформация.

И редактировать число стволов у пушки, приделывать свистелки и т.п. можно будет вообще
не трогая код.
Отредактировано 20.07.2017 16:11 dmz . Предыдущая версия . Еще …
Отредактировано 20.07.2017 16:00 dmz . Предыдущая версия .
Отредактировано 20.07.2017 15:57 dmz . Предыдущая версия .
Re[10]: проектирование
От: neFormal Россия  
Дата: 20.07.17 21:17
Оценка:
Здравствуйте, dmz, Вы писали:

dmz>Один из них, например — взять значение, и выдернуть из него все спрайты

dmz>которые и отрисовать скопом:

у них есть порядок, заданный в коде.
а что с остальными 8999?

F>>можно завести себе поле "parent" и обращаться к нему, но в случае нескольких уровней потомков нельзя вызвать отрисовку, обратившись прозрачно "насквозь" через несколько родителей.

F>>вот вызов методов далёких предков я и ищу
dmz>Алё, тут нету наследования. Какие еще предки. Это же хаскел.

алё, ты всегда можешь объявить поле/метод со значением типа предка. чтобы потом к нему обращаться и вызывать ф-ции "базовых типов".

dmz>но ничего, живут как-то. Алсо глубокие наследования вроде бы ```considered harmful```,

dmz>и как свидетель системы с глубокими цепочками наследования --- соглашусь.

проблема в том, что в х-е 2+ уровня — это уже `considered harmful`
судя по синтаксису

dmz>Ну и вообще --- мне тут видится подвох. С каких это пор графические объекты в играх

dmz>композируют в коде из спрайтов? А не рисуют/моделируют/размечают в редакторе? Скорее
dmz>всего, IRL тут будет таки один спрайт, смоделированный в редакторе, и какая-то
dmz>метаинформация.
dmz>И редактировать число стволов у пушки, приделывать свистелки и т.п. можно будет вообще
dmz>не трогая код.

да, вариант с наследованием и ручной отрисовкой — это из старых джвижков
в современных порядочно EntityComponentSystem, где как раз как-то это размечается в редакторе с метаинфой и автоматической отрисовкой.
сферически это так, да. пока не посмотришь поближе :3 если есть решение через паттерны, то я тоже буду за него благодарен.
...coding for chaos...
Re[5]: проектирование
От: neFormal Россия  
Дата: 20.07.17 21:43
Оценка:
Здравствуйте, dmz, Вы писали:

dmz>Но IRL, конечно же, выяснится что никакую пушку из двух пушек никто никогда не композирует для

dmz>рисования, и что спрайты с фазами или векторная модель отдельно — и для неё вообще неважно,
dmz>двухствольная это пушка или гатлинг, активные точки, которые как-то взаимодействуют — отдельно
dmz>и их изначально N штук, ракеты отдельно и так далее.

не, это всё реальные ситуации.
вот у меня сейчас, например, если игрушка уровня Героев3, где в зависимости от повешенного баффа меняется поведение.
и тут драма в том, что в коде это не предполагается. например, нельзя в зависимости от баффа отобрать у игрока ход и походить автоматически.
гейдев — это такая сфера, где механика может меняться раз в день, потому что ГД воспринимают всё внутреннее поведение, как некую магию. типа, вжжжух, и что-то случилось.
мой пример может выродиться такой случай, что игрок управляет одной пушкой, а вторая работает автоматически. сама выбирает цель и отакует. а может нет.
поэтому это очень жизненно. я с этим примером пристаю к хакселистам, и пока не нашёл решения.

dmz>А все эти истории про наследование двухствольной пушки от одноствольной, кенгуру с ПЗРК,

dmz>наследование квадрата от прямоугольника или прямоугольника от квадрата — не более, чем
dmz>народные анекдоты и сказки дедушки Гради Буча.

ладно, это мб слишком тяжело.
подскажи лучше по онтопику. знаешь ли книжки/мануалы по проектированию в ФП? мне интересно для развития.
может я чего-то не понимаю ещё.
...coding for chaos...
Re[11]: проектирование
От: dmz Россия  
Дата: 21.07.17 03:55
Оценка:
dmz>>Один из них, например — взять значение, и выдернуть из него все спрайты
dmz>>которые и отрисовать скопом:

F>у них есть порядок, заданный в коде.

F>а что с остальными 8999?

Ну так задать порядок в типе, или выводимый из типа, очевидно.

F>алё, ты всегда можешь объявить поле/метод со значением типа предка. чтобы потом к нему обращаться и вызывать ф-ции "базовых типов".


Не очень понял — можешь код показать? Что есть предок, например. Тут есть только композиция.
Конечно, можно напилить что-то вроде:

data Grandpa = Grapnda {...}
data Father = Father { grandpa :: Grandpa, fatherData = ... }
data Son    = Son { father = Father, sonData = ... }

-- и потом делать как-то так:

instance Drawable Son where
  draw (s@Son{..}) canvas = do
    draw (father son) -- будет и отрисовка для "предков", если инстанс для Father определен таким образом
    -- отрисовка для Son


Но это калька с Java-style ООП и так обычно не делают. Я бы просто сделал так,
что бы каждый тип содержал коллецию своих спрайтов, и выдергивал бы эти спрайты
с их атрибутами через syb. Включая порядок, который тоже можно задать разными
способами.


data OneBarrelGun = OneBarrelGun { sprites :: [Sprite] } 

-- тоже WTF, калька с Java
-- чего б нам просто не перечислить нужные спрайты и не тащить сюда
-- OneBarrelGun, если он нам не нужен на самом деле

data TwoBarrelGun = TwoBarrelGun { oneBarrel :: OneBarrelGun, twoBarrelSprites = [Sprite] } 

...


instance Monoid Canvas where

...

-- Template Method!
-- на самом деле, просто функция, которая имеет дело
-- с типами, для которых есть реализации нужных тайпклассов

draw :: (MonadDraw m, Data a) => a -> Canvas -> m Canvas
draw a = do
  let sprites = sortBy spriteOrder [ x | x@(Sprite{..}) <- universeBi a ]
  return $ foldMap (renderSprite emptyCanvas) sprites



F>проблема в том, что в х-е 2+ уровня — это уже `considered harmful`

F>судя по синтаксису

Ну в языке, где вообще нет никакого наследования --- надо полагать.

F>да, вариант с наследованием и ручной отрисовкой — это из старых джвижков

F>в современных порядочно EntityComponentSystem, где как раз как-то это размечается в редакторе с метаинфой и автоматической отрисовкой.
F>сферически это так, да. пока не посмотришь поближе :3 если есть решение через паттерны, то я тоже буду за него благодарен.

Ну вот, привёл несколько вариантов. Да, тут нет наследования, vtbl и объектов, как смеси кода и данных.
Если всё это хочется, то надо, очевидно, использовать язык, где оно есть, зачем тогда Haskell ?

И еще надо учесть, что тут код отдельно — данные отдельно. Поэтому "наследование" в смысле Java-style OOP
особого смысла не имеет.
Отредактировано 21.07.2017 4:09 dmz . Предыдущая версия .
Re[6]: проектирование
От: dmz Россия  
Дата: 21.07.17 04:06
Оценка:
F>ладно, это мб слишком тяжело.

Пока не вижу ничего тяжелого. Т.е обычный подход — написать, что бы работало,
а если стало плохо, то отрефакторить.

Тут рефакторинг работает, потому, что есть типы.

F>подскажи лучше по онтопику. знаешь ли книжки/мануалы по проектированию в ФП? мне интересно для развития.

F>может я чего-то не понимаю ещё.

Книжку не знаю, но обычно тут работает KISS

Т.е всё, что ты хотел сделать, вроде бы делается тем или иным способом.
Если видишь, что это приводит к бойлерплейту — просто обобщаешь и рефакторишь.

Обычные книжки вроде Лармана или Фаулера тут вполне работают, по крайней мере я в них,
бывает, поглядываю, когда делаю что-то жирное энтерпрайзное. Процентов 5 из них применимо.

Вопросы про паттерны и книжку по проектированию периодически поднимаются, но проблема в том,
что для архитектуры уровня систем/сервисов — подходят обычные знания, как у всех, а для
микроархитектуры того уровня, где важны "объекты" и "классы" работают принципы KISS и JFGID.

Может, полезен будет Рэймонд с его Data-driven design. Потому что здесь во многом он.

Надо еще учесть, что язык уровнем повыше, в нем есть много интересного для уменьшения кодобазы,
типа Generic и SYB, поэтому система, которая будет большой на C++, тут будет всё еще маленькой
в плане LoC.
Отредактировано 21.07.2017 4:10 dmz . Предыдущая версия .
Re[6]: проектирование
От: dmz Россия  
Дата: 21.07.17 04:19
Оценка: +1
F>мой пример может выродиться такой случай, что игрок управляет одной пушкой, а вторая работает автоматически. сама выбирает цель и отакует. а может нет.
F>поэтому это очень жизненно. я с этим примером пристаю к хакселистам, и пока не нашёл решения.

Может быть, потому, что пример слишком общий и расплывчатый, и его можно реализовать
9000 способами, которые зависят от выбранных подходов. И в общем виде трудно что-то сказать
при такой постановке задачи.

Например, вот "герой управляет vs сама собой управляет". Очевидно, что оно управляется
потоком событий. И какая-то реализация "пушки" реагирует на события от "героя". А какая-то нет.

Но, очевидно, если это реализуется на Си (брейнфаке), то и на хаскелле тоже.
Re[12]: проектирование
От: neFormal Россия  
Дата: 24.07.17 10:36
Оценка:
Здравствуйте, dmz, Вы писали:

F>>алё, ты всегда можешь объявить поле/метод со значением типа предка. чтобы потом к нему обращаться и вызывать ф-ции "базовых типов".

dmz>Не очень понял — можешь код показать? Что есть предок, например. Тут есть только композиция.

я про такой Extends:
data BaseObject = BaseObject { img :: Image.Sprite } deriving (Show)

class Extends a where
  type BaseType a
  super :: a -> BaseType a

data Gun = Gun { gunBase :: BaseObject, gunDamage :: Int} deriving (Show)
instance Extends Gun where
  type BaseType Gun = BaseObject
  super = gunBase
instance SceneObject.SceneObject Gun where
  draw gun delta = do
    Image.draw (img . super $ gun) delta
    return ()
  update gun = do {print "gun update"}

data Bullet = Bullet { bulletBase :: BaseObject, bulletDamage :: Int} deriving (Show)
instance Extends Bullet where
  type BaseType Bullet = BaseObject
  super = bulletBase
instance SceneObject.SceneObject Bullet where
  draw bullet delta = do
    Image.draw (img . super $ bullet) delta
    return ()
  update bullet = do {print "bullet update"}

data Bullet2 = Bullet2 { bullet2Base :: Bullet } deriving (Show)
instance Extends Bullet2 where
  type BaseType Bullet2 = Bullet
  super = bullet2Base
instance SceneObject.SceneObject Bullet2 where
  draw bullet delta = do {print "bullet 2 render"}
  update bullet = do { print "bullet 2 update and:"; SceneObject.update $ super bullet}


dmz>Но это калька с Java-style ООП и так обычно не делают. Я бы просто сделал так,

dmz>что бы каждый тип содержал коллецию своих спрайтов, и выдергивал бы эти спрайты
dmz>с их атрибутами через syb. Включая порядок, который тоже можно задать разными
dmz>способами.

да, я понимаю. поэтому вопрос в том, как это сделать идеологически верно.

dmz>-- тоже WTF, калька с Java

dmz>-- чего б нам просто не перечислить нужные спрайты и не тащить сюда
dmz>-- OneBarrelGun, если он нам не нужен на самом деле

хочется реюзать поведение и данные. это основная мотивация.

если это можно сделать через композицию, то прекрасно. мне всё равно как организовывать данные и ф-ции, если это решает проблему.
я просто не вижу, как можно одновременно реюзать и то, и другое. только по отдельности. а по отдельности меня не устраивает.
...coding for chaos...
Re[2]: проектирование
От: VladD2 Российская Империя www.nemerle.org
Дата: 16.08.17 12:24
Оценка:
Здравствуйте, Nick Linker, Вы писали:

NL>Вообще в двух словах не расскажешь, так что если есть что-то более специфическое спросить, то лучше так.


Так может стоит написать статейку? Мы ее опубликуем, а народ обсудит.
Есть логика намерений и логика обстоятельств, последняя всегда сильнее.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.