singalen: (portrait)
[personal profile] singalen
Хороший человек [livejournal.com profile] vbayda показал, как он использует boost::bind:
if (entityes_.end() == std::find_if(entityes_.begin(), entityes_.end(),
     boost::bind < bool > (&intersect < double >, c,
         boost::bind(&world::positions::entity::shape_,
            boost::bind(&world::entity::positions_, _1)
         )
     )
    ))

Как на меня, мягко выражаясь, выглядит не слишком читабельно.
Понял слова Алана Перлиса "избыток синтаксического сахара приводит к раку фигурных скобок" (переврал цитату, и ну его).
Не слишком-то C++ пригоден для функциональщины. Правда, синтаксис получился не намного хуже, чем у Lisp-а.

Попытался переписать это на Haskell. Не могу скомпилировать, хоть ты тресни. Хотя выглядит немного лучше.
Комментирую синтаксис "в стиле учебника" для тех, кто не знает языка.
module Main
  where

-- это описание типа данных - "записи" с конструктором и одним полем
data Entity = CreateEntity { position :: Position }

data Position = CreatePosition { circle :: Circle }

data Circle = CreateCircle { x, y, r :: Float }

-- указание типа функции (аргументов и параметров).
-- Обычно можно опустить, type inferrance его определяет.
circlesIntersect :: Circle -> Circle -> Bool
-- это собственно описание функции: имя, параметры, =, тело-выражение
circlesIntersect c1 c2 = sqr (r c1 + r c2) < sqr (x c1 - x c2) + sqr (y c1 - y c2)
  where sqr x = x*x

findIntersecting :: Circle -> [Entity] -> Entity
findIntersecting circle entities = 
-- это тот самый байнд одного (первого) параметра. Выглядит так: (имя функции) параметр.
  find ((entityMatches) circle) entities
  where
    entityMatches :: Circle -> Entity -> Bool
    entityMatches c e = circlesIntersect c (circle position e)
-- Оказывается, в Haskell обращение к полям структуры выглядит не как c.x, а как x(c).
-- Или x c, что в этом случае то же самое.

main = putStr "Hello\n"

Итого получается (получилось бы, если бы я лучше понимал синтаксис и типизацию)
findIntersecting circle entities = 
  find ((entityMatches) circle) entities where
    entityMatches c e = circlesIntersect c (circle position e)

или, без промежуточной "объясняющей" функции
findIntersecting circle entities = 
  find (((circlesIntersect) c (circle position e)) circle) entities

Считаем скобки, почти как в Лиспе. Кстати, синтаксис Лиспа должен был быть похож на хаскельный. Но его где-то там не смогли заимплементить и бросили нынешнем виде, который предназначался только для списковых данных.
Posted by [identity profile] migmit.livejournal.com
nis> В Хаскеле для обобщения требуется наоборот сократить спецификации и упростить код.
Я написал "иногда". В общем случае, нужны более серьёзные телодвижения. Хотя к спецификации типов они имеют весьма опосредованное отношение.
nis> В "скриптовых" языках все не только умудряются жить "без типов", но и даже находят в этом определенные преимущества.
Здесь следует различать. Есть языки со слабой типизацией - например, Perl. Там вполне можно написать 2+"three" и получить более-менее осмысленный результат. ИМХО, такой подход имеет право на существование, если мы пишем очень маленькие скрипты для выполнения небольших задач. Пожалуй, единственное значимое исключение здесь (из известных мне) - Tcl, пригодный для относительно крупных вещей, благодаря некоей объединяющей идее ("всё есть строка"). На самом деле, там с типами тоже не всё так просто, и перловской мешанины там нет.
А есть языки с сильной, но динамической типизацией. Где рассогласование типов вызывает ошибку времени ИСПОЛНЕНИЯ. И вот здесь появляются наиболее мощные вещи - Lisp, SmallTalk, JavaScript (ох... вынужден я сейчас писать на некоем диалекте жабаскрипта - тех, кто его (диалект) придумывал, убить мало; надо ж так кастрировать замечательный язык). Вот они, как раз, пригодны для очень серьёзных проектов.
Я бы сказал так. Языки со статической сильной типизацией (тот же Haskell, например; C++ к ним не относится, там типизация слабая) хороши тогда, когда мы совершенно чётко представляем себе, что мы пишем. Если же проект предполагает некое исследование возможностей, если могут случиться очень серьёзные изменения дизайна - нужен язык с динамической сильной типизацией. Языки же со слабой типизацией нужны для маленьких вещей, где можно обойтись без дизайна вообще и сделать нужную программу на интуиции.
nis> Вы имеете ввиду такой пример? Скажем в стеке вызовов функций все функции принимают произвольный тип, но несуществующий интерфейс требует скажем только самая нижняя (самая внутренняя) функция. В этом случае компилятор выдаст ошибку во внутренней функции и стек вызовов придется отслеживать самому. А в случае явной спецификации компилятор укажет на ошибку сразу на самом верху?
Да, например, вполне возможный сценарий. Именно поэтому в Лиспе, например, предусмотрено объявление типов переменных (хотя и является необязательным).