Haskell - образец, соответствующий синтаксическому сахару, и где
Часто у меня есть функция такого шаблона:
f :: a -> b
f x = case x of
... -> g ...
... -> g ...
...
... -> g ...
where g = ...
В этом случае существует синтаксический сахар:
f :: a -> b
f ... = g ...
f ... = g ...
...
f ... = g ...
К сожалению, я не могу прикрепить к нему where
: я, очевидно, получаю кучу not in scope
s.
Я могу сделать g
отдельную функцию, но это не приятно: мое пространство имен модулей будет загрязнено функциями утилиты.
Есть ли способ обхода?
Ответы
Ответ 1
Я думаю, что ваш первый пример неплох. Единственный синтаксический вес: case x of
, плюс ->
вместо =
; последнее компенсируется тем фактом, что вы можете опустить имя функции для каждого предложения. Действительно, даже предложенная dflemstr go
вспомогательная функция синтаксически тяжелее.
По общему признанию, это немного противоречиво по сравнению с синтаксисом предложения функции normal, но это, вероятно, хорошо: он более точно визуально ограничивает область, в которой доступен x
.
Ответ 2
Нет, нет обходного пути. Когда у вас есть несколько предложений для такой функции, они не могут использовать where
-clause. Ваш единственный вариант - использовать оператор case или сделать что-то вроде этого:
f x =
go x
where
go ... = g ...
go ... = g ...
g = ...
... если вы действительно хотите использовать форму функции по какой-либо причине.
Ответ 3
f = g . h -- h is most of your original f
where h ... = ...
h ... = ...
g =
Ответ 4
Из Haskell 2010 on или с GHC вы также можете:
f x
| m1 <- x = g
| m2 <- x = g
...
where g =
но обратите внимание, что вы не можете использовать переменные, связанные с шаблонами в g
. Это эквивалентно:
f x = let g = ... in case () of
() -> case x of
m1 -> g
_ -> case x of
m2 -> g
....
Ответ 5
Ваше оригинальное решение кажется лучшим и единственным решением. Синтаксически это не тяжелее прямого сопоставления шаблонов по функциональным параметрам, если даже не светлее.
Но на всякий случай, если вам нужно только проверить предварительные условия, а не совпадение с шаблоном, не забывайте о стражах, которые позволяют вам свободно обращаться к области where
. Но на самом деле я не вижу ничего плохого в вашем решении case of
.
f :: a -> b
f a
| a == 2 = ...
| isThree a = ...
| a >= 4 = ...
| otherwise = ...
where isThree x = x == 3
Ответ 6
Можно ли предположить, что вы последовательно используете g
для большинства, если не всех, разных ветвей аргумента case?
Работая с предположением, что f :: a -> b
для некоторых a
и b
(возможно, полиморфных), g
обязательно является некоторой функцией формы c -> d
, что означает, что должен быть способ последовательно извлекать a c
из a
. Назовите это getC :: a -> c
. В этом случае решением было бы просто использовать h . g . getC
для всех случаев, где h :: d -> b
.
Но предположим, что вы не всегда можете получить c
из a
. Возможно, a
имеет вид f c
, где f
есть Functor
? Тогда вы могли бы fmap g :: f c -> f d
, а затем каким-то образом преобразовать f d
в b
.
Просто беспорядок здесь, но fmap
было первым, что пришло мне в голову, когда я увидел, что вы, кажется, применяете g
для каждой ветки.
Ответ 7
С помощью LambdaCase
вы также можете сделать это:
{-# language LambdaCase #-}
f :: a -> b
f = \case
... -> g ...
... -> g ...
...
... -> g ...
where g = ...