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. Я могу сделать g отдельной функцией, но это некрасиво: пространство имен моего модуля будет засорено служебными функциями. Есть ли обходной путь?


person Matvey Aksenov    schedule 19.01.2012    source источник
comment
Вам не нужно экспортировать g, поэтому загрязнение пространства имен является проблемой только внутри вашего собственного модуля.   -  person Dan Burton    schedule 20.01.2012


Ответы (7)


Я думаю, что ваш первый пример совсем не плох. Единственный синтаксический вес — это case x of плюс -> вместо =; последнее компенсируется тем фактом, что вы можете опустить имя функции для каждого предложения. Действительно, даже предложенная dflemstr вспомогательная функция go синтаксически тяжелее.

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

person ehird    schedule 19.01.2012
comment
Эквациональный синтаксис для определения функций кажется ненужным трюком. - person Tom Crockett; 20.01.2012

Нет, обходного пути нет. Когда у вас есть несколько предложений для такой функции, они не могут использовать совместное предложение where. Ваш единственный вариант - использовать оператор case или сделать что-то вроде этого:

f x =
  go x
  where
    go ... = g ...
    go ... = g ...
    g = ...

...если вы действительно по какой-то причине хотите использовать функциональную форму.

person dflemstr    schedule 19.01.2012

Начиная с Haskell 2010 или с помощью 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
              ....
person Ingo    schedule 19.01.2012

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

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

f :: a -> b
f a
  | a == 2    = ...
  | isThree a = ...
  | a >= 4    = ...
  | otherwise = ...
  where isThree x = x == 3
person Nikita Volkov    schedule 19.01.2012

С LambdaCase вы также можете сделать это:

{-# language LambdaCase #-}

f :: a -> b
f = \case
  ... -> g ...
  ... -> g ...
  ...
  ... -> g ...
  where g = ...
person Wizek    schedule 19.01.2018

Безопасно ли предположить, что вы постоянно используете g в большинстве, если не во всех, различных ветвях оператора case?

Опираясь на предположение, что f :: a -> b для некоторых a и b (возможно, полиморфных), g обязательно является некоторой функцией формы c -> d, а это означает, что должен существовать способ последовательного извлечения 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 к каждой ветке.

person Dan Burton    schedule 20.01.2012

person    schedule
comment
Я думаю, что ОП искал общее решение проблемы, когда g может появиться где угодно в правой части любого из уравнений (или вообще не появиться). - person Tom Crockett; 20.01.2012
comment
Да, я почувствовал, что вопрос был двусмысленным, и дал ответ на тот случай, на который еще не было ответа. - person dave4420; 20.01.2012