developer tip

Haskell : Where vs. Let

optionbox 2020. 8. 10. 07:57
반응형

Haskell : Where vs. Let


나는 Haskell을 처음 접했고 Where vs. Let에 대해 매우 혼란 스럽습니다 . 둘 다 비슷한 목적을 제공하는 것 같습니다. WhereLet 사이의 몇 가지 비교를 읽었 지만 언제 각각을 사용해야하는지 구분하는 데 어려움이 있습니다. 누군가가 컨텍스트를 제공하거나 다른 것을 사용할 때를 보여주는 몇 가지 예를 제공 할 수 있습니까?

어디 대하자

where절은 함수 정의의 수준에서 정의 할 수 있습니다. 일반적으로 let정의 범위와 동일합니다 . 유일한 차이점은 가드를 사용할 때 입니다. where절의 범위는 모든 가드에 걸쳐 있습니다. 반대로 let표현식 의 범위 는 현재 함수 절과 가드 (있는 경우)뿐입니다.

Haskell 치트 시트

하스켈 위키는 매우 상세하고 다양한 사례를 제공하지만 가상의 예를 사용합니다. 초보자에게는 설명이 너무 짧습니다.

Let의 장점 :

f :: State s a
f = State $ \x -> y
   where y = ... x ...

Control.Monad.State

where는 f = 일치하는 패턴을 나타내므로 작동하지 않습니다. 여기서 x는 범위에 없습니다. 반대로 let으로 시작했다면 문제가 없을 것입니다.

Let의 장점에 대한 Haskell Wiki

f :: State s a
f = State $ \x ->
   let y = ... x ...
   in  y

Where의 장점 :

f x
  | cond1 x   = a
  | cond2 x   = g a
  | otherwise = f (h x a)
  where
    a = w x

f x
  = let a = w x
    in case () of
        _ | cond1 x   = a
          | cond2 x   = g a
          | otherwise = f (h x a)

선언 대 표현

Haskell wiki는 Where 절이 선언적이지만 Let 표현식은 표현 적이 라고 언급합니다 . 스타일 외에도 어떻게 다르게 수행합니까?

Declaration style                     | Expression-style
--------------------------------------+---------------------------------------------
where clause                          | let expression
arguments LHS:     f x = x*x          | Lambda abstraction: f = \x -> x*x
Pattern matching:  f [] = 0           | case expression:    f xs = case xs of [] -> 0
Guards:            f [x] | x>0 = 'a'  | if expression:      f [x] = if x>0 then 'a' else ...
  1. 첫 번째 예에서 Let 은 범위에 있지만 어디에 있지 않습니까?
  2. 첫 번째 예에 Where 를 적용 할 수 있습니까 ?
  3. 변수가 실제 표현을 나타내는 실제 예제에 이것을 적용 할 수 있습니까?
  4. 각각을 사용할 때 따라야 할 일반적인 경험 법칙이 있습니까?

최신 정보

나중에이 스레드에서 오는 사람들을 위해 여기에서 찾을 수있는 최상의 설명을 찾았습니다 : " A Gentle Introduction to Haskell ".

식하자.

Haskell의 let 표현식은 중첩 된 바인딩 세트가 필요할 때 유용합니다. 간단한 예로서 다음을 고려하십시오.

let y   = a*b
    f x = (x+y)/y
in f c + f d

let 표현식에 의해 생성 된 바인딩 세트는 상호 재귀 적이며 패턴 바인딩은 지연 패턴으로 처리됩니다 (즉, 암시 적 ~을 전달 함). 허용되는 유일한 선언 유형은 형식 서명, 함수 바인딩 및 패턴 바인딩입니다.

Where 절.

Sometimes it is convenient to scope bindings over several guarded equations, which requires a where clause:

f x y  |  y>z           =  ...
       |  y==z          =  ...
       |  y<z           =  ...
     where z = x*x

Note that this cannot be done with a let expression, which only scopes over the expression which it encloses. A where clause is only allowed at the top level of a set of equations or case expression. The same properties and constraints on bindings in let expressions apply to those in where clauses. These two forms of nested scope seem very similar, but remember that a let expression is an expression, whereas a where clause is not -- it is part of the syntax of function declarations and case expressions.


1: The problem in the example

f :: State s a
f = State $ \x -> y
    where y = ... x ...

is the parameter x. Things in the where clause can refer only to the parameters of the function f (there are none) and things in outer scopes.

2: To use a where in the first example, you can introduce a second named function that takes the x as a parameter, like this:

f = State f'
f' x = y
    where y = ... x ...

or like this:

f = State f'
    where
    f' x = y
        where y = ... x ...

3: Here is a complete example without the ...'s:

module StateExample where

data State a s = State (s -> (a, s))

f1 :: State Int (Int, Int)
f1 = State $ \state@(a, b) ->
    let
        hypot = a^2 + b^2
        result = (hypot, state)
    in result

f2 :: State Int (Int, Int)
f2 = State f
    where
    f state@(a, b) = result
        where
        hypot = a^2 + b^2
        result = (hypot, state)

4: When to use let or where is a matter of taste. I use let to emphasize a computation (by moving it to the front) and where to emphasize the program flow (by moving the computation to the back).


While there is the technical difference with respect to guards that ephemient pointed out, there is also a conceptual difference in whether you want to put the main formula upfront with extra variables defined below (where) or whether you want to define everything upfront and put the formula below (let). Each style has a different emphasis and you see both used in math papers, textbooks, etc. Generally, variables that are sufficiently unintuitive that the formula doesn't make sense without them should be defined above; variables that are intuitive due to context or their names should be defined below. For example, in ephemient's hasVowel example, the meaning of vowels is obvious and so it need not be defined above its usage (disregarding the fact that let wouldn't work due to the guard).


Legal:

main = print (1 + (let i = 10 in 2 * i + 1))

Not legal:

main = print (1 + (2 * i + 1 where i = 10))

Legal:

hasVowel [] = False
hasVowel (x:xs)
  | x `elem` vowels = True
  | otherwise = False
  where vowels = "AEIOUaeiou"

Not legal: (unlike ML)

let vowels = "AEIOUaeiou"
in hasVowel = ...

I found this example from LYHFGG helpful:

ghci> 4 * (let a = 9 in a + 1) + 2  
42  

let is an expression so you can put a let anywhere(!) where expressions can go.

In other words, in the example above it is not possible to use where to simply replace let (without perhaps using some more verbose case expression combined with where).


Sadly, most of the answers here are too technical for a beginner.

LHYFGG has a relevant chapter on it -which you should read if you haven't already, but in essence:

  • where is just a syntactic construct (not a sugar) that are useful only at function definitions.
  • let ... in is an expression itself, thus you can use them wherever you can put an expression. Being an expression itself, it cannot be used for binding things for guards.

Lastly, you can use let in list comprehensions too:

calcBmis :: (RealFloat a) => [(a, a)] -> [a]
calcBmis xs = [bmi | (w, h) <- xs, let bmi = w / h ^ 2, bmi >= 25.0]
-- w: width
-- h: height

We include a let inside a list comprehension much like we would a predicate, only it doesn't filter the list, it only binds to names. The names defined in a let inside a list comprehension are visible to the output function (the part before the |) and all predicates and sections that come after of the binding. So we could make our function return only the BMIs of people >= 25:

참고URL : https://stackoverflow.com/questions/4362328/haskell-where-vs-let

반응형