Попытка моего новичка выразить поведение operator || из C/C++ в Haskell:
orElse :: Bool -> (a -> Bool) -> (a -> Bool)
orElse True _ = const True
orElse False pred = pred
Можно ли это обобщить, чтобы принять a -> b -> Bool, a -> b -> c -> Bool и т. д. в качестве второго аргумента?
Посмотрел техники перегрузки, ХКЦ ет. ал. - Кажется, ничто не связано с обобщением по роду...
Редактировать: теперь я понимаю, почему это был плохой пример (помогло чтение определения operator|| в прелюдии), но я все еще задаюсь вопросом о более широком вопросе в том виде, в каком он был поставлен.
Аналогия с operator|| — это последовательность: если левая сторона равна True, то правая сторона не оценивается, независимо от требований строгости. Возможно, я здесь совершенно не в теме... предполагаемое использование было примерно таким: anyOf :: Foldable f => (a -> Bool) -> f a -> BoolanyOf pred = foldl' (flip orElse pred) Falseelem' :: (Foldable f, Eq a) => a -> f a -> Boolelem' y = anyOf (== y)
Haskell || уже ленив во втором аргументе...
Хорошо, этот конкретный случай является спорным - спасибо! Возникает более широкий вопрос: возможно ли вообще работать с функциями разных типов? Или это просто то, что никогда не требуется по разным причинам, например, указанным выше?
Я представлял себе использование семейства типов для рекурсивного распознавания функций, которые в конечном итоге возвращают Bool, но я не уверен, как (или если бы) вы это написали.





Вероятно, вы можете создать нужную функцию, используя классы типов, но я не уверен, что это хорошая идея.
class MagicOr a where
magicOr :: a -> a -> a
instance MagicOr Bool where
magicOr = (||)
instance MagicOr b => MagicOr (a -> b) where
magicOr f g = \x -> f x `magicOr` g x
-- or, alternatively,
-- magicOr = liftA2 magicOr
В приведенном выше коде определяется функция magicOr, которую можно использовать с двумя аргументами типа Bool, типа a -> Bool, типа a -> b -> Bool и т. д.
На данный момент я думаю, что этот код не так уж и плох и вполне пригоден для использования.
Тем не менее, в зависимости от того, что вам действительно нужно сделать, вы можете в конечном итоге создать сложный механизм классов типов, который может быть несколько хрупким в использовании. Пример: printf. Его определение довольно сложно, поскольку ему приходится обрабатывать неизвестное количество параметров. Кроме того, при неправильном использовании это может сбить с толку программу проверки типов, которая будет выдавать сообщения об ошибках, которые трудно понять.
Конечно, не стесняйтесь экспериментировать и убедитесь сами. Имейте в виду, что слишком частое злоупотребление классами типов иногда приводит к громоздкому коду.
Спасибо! Думаю, я понимаю здесь шаблон типовых классов...
Обратите внимание на сходство с экземплярами Semigroup для Any и для функций. Единственная разница здесь в том, что вы избегаете newtype.
@NaïmFavier Хорошая мысль. При использовании a -> b -> ... -> Any мой magicOr равен просто (<>). А Any — это просто Bool с другим именем (так что используется операция или-полугруппа).
Как это связано с
operator ||? Я ожидалorElse :: Bool -> Bool -> Bool--- зачем дополнительный аргументaи почему только второй аргумент?