Comments 9
data Figure a = forall a. Paint a => Figure a
Мне кажется, или это должно быть
data Figure = forall a. Paint a => Figure a
т.е. у тайпконструктора Figure не должно быть параметра? Ведь он квантифицируется forall'ом, следовательно, это связанная типовая переменная внутри самого определения типа.
Спасибо за статью, как раз недавно разбирался немного с хаскеллом.
Пример слегка демотивирует — выглядит громоздко и нечитаемо по сравнению с ООП-версией.
Пример слегка демотивирует — выглядит громоздко и нечитаемо по сравнению с ООП-версией.
Для этого я воспользуюсь расширением ExistentialQuantification, которое позволяет объединять вместе с данными
И получить антипаттерн.
А если вспомнить, что объекты – это замыкания для бедных, требуемую задачу можно изобразить без каких-либо языковых расширений:
import System.IO
import Text.Printf
data Figure = Figure
{ paint :: Handle -> IO ()
, say :: String
, circumSquare :: Int
}
base child = child
{ paint = \handle -> hPutStrLn handle $ printf "paint %s S=%d" (say child) (circumSquare child)
}
type Point = (Int, Int)
data Rect = Rect {left, top, right, bottom :: Int}
deriving Show
makeRect :: Point -> Point -> Rect
makeRect (left, top) (right, bottom) = Rect left top right bottom
circle :: Point -> Int -> Figure
circle (x, y) radius = base $ Figure
{ say = printf "circle, radius=%d and centre=(%d,%d)" radius x y
, circumSquare = (2 * radius) ^ 2
}
rect :: Point -> Point -> Figure
rect lt@(left, top) rt@(right, bottom) = base $ Figure
{ say = show $ makeRect lt rt
, circumSquare = (right - left) * (bottom - top)
}
roundrect :: Point -> Point -> Int -> Figure
roundrect lt rt roundR = (rect lt rt)
{ say = printf "round rectangle, %s and roundR = %d" (show $ makeRect lt rt) roundR
}
Рад Вашему энтузиазму. (Без сарказма). Статью антипаттерн читал по ссылки с dev.stephendiehl.com/hask — это частное мнение, а не абсолют. То, что задачу можно решить разными способами, не сомневаюсь. И разные варианты имеют свои недостатки. Например, у Вас исходные координаты теряются, превращаясь сразу в строки. Конкретный пример решается, но, если потребуется координаты использовать разными способами, то станет посложнее (хотя, тоже решается). Я знаю и то что, в Haskell, вобщем то, не стОит применять ООП-шаблоны, а использовать Haskell-евские приёмы. Однако, есть тенденция — arxiv.org/pdf/cs/0509027v1.pdf
Кратко: у меня пример, демонстрирующий некоторые языковые расширения и использование классов типов. У Вас другой пример. Ничего не имею против. Поставлю лайк на Ваш ответ.
Кратко: у меня пример, демонстрирующий некоторые языковые расширения и использование классов типов. У Вас другой пример. Ничего не имею против. Поставлю лайк на Ваш ответ.
Что значит «теряются» (если они вон используются в нескольких местах), что значит «сразу» (особенно учитывая ленивую модель)?
Это да. Однако обсуждения – они в первую очередь для «зрителей», и если забредший на функциональный огонёк неофит ужаснётся, сколько всего нужно накрутить, чтобы решить такую простую в ОО-языке задачу, то ему стоит увидеть и альтернативный подход.
Возможно, статье лучше бы подошло название вроде «Эмуляция традиционного ООП на языке Haskell».
То, что задачу можно решить разными способами, не сомневаюсь.
Это да. Однако обсуждения – они в первую очередь для «зрителей», и если забредший на функциональный огонёк неофит ужаснётся, сколько всего нужно накрутить, чтобы решить такую простую в ОО-языке задачу, то ему стоит увидеть и альтернативный подход.
Возможно, статье лучше бы подошло название вроде «Эмуляция традиционного ООП на языке Haskell».
Что значит «теряются» (если они вон используются в нескольких местах)
В смысле, что их не получить из списка [Figure], т.е. мы вынуждены реализовать все возможные действия как функции в Figure, т.е. объединяем хранение с логикой и представлением.
Возможно, статье лучше бы подошло название вроде «Эмуляция традиционного ООП на языке Haskell».
Мне кажется, что эмуляция — это, например, wiki.haskell.org/OOP_vs_type_classes, п.5. По классу типов на каждый тип данных.
То что у меня тоже сохраняются исходные данные и приходится использовать ExistentialQuantification — ну, не является оно абсолютным злом. По Вашей же ссылке: «Замыкания — это объекты для бедных!». Я, конечно, так не считаю, но название пока оставлю. Вставлю в начало статьи упоминание о приведённой ниже Вашей альтернативе.
В смысле, что их не получить из списка [Figure]
Так и в вашем коде не получить. Т.е. получить можно, но всё, что можно сделать — это позвать функции класса Paint, разве нет?
data Figure = forall x . (Typeable x, Paint x) => Figure x
Далее безопасно кастуете и выбираете наздоровье. В добавок можно ещё и красивые линзы прикрутить.
В целом использование явного словаря
data D = D { ... }
или невного класса-типов D
это достаточно близкие методы, с небольшой разницей, вроде той, что в случае класса типов, для одного параметра у нас гарантируется наличие единственной реализации функций, чего нету в случае явного словаря. Так-же из функций класса типов мы можем достать сам словарь Dict (c::Constraint)
и передавать его явно или восстановить его по прокси переменной. Все тоже самое возможно и с явным словарём, но с большим количеством страданий.Sign up to leave a comment.
Пример решения типичной ООП задачи на языке Haskell