web-dev-qa-db-fra.com

Haskell: Où contre Let

Je suis nouveau à Haskell et je suis très confus par vs Soit . Ils semblent tous deux fournir un objectif similaire. J'ai lu quelques comparaisons entre vs Soit mais je suis avoir du mal à discerner quand les utiliser. Quelqu'un pourrait-il s'il vous plaît fournir un certain contexte ou peut-être quelques exemples qui montrent quand utiliser l'un sur l'autre?

Où vs Let

Une clause where ne peut être définie qu'au niveau d'une définition de fonction. Habituellement, cela est identique à la portée de la définition de let. La seule différence est lorsque les gardes sont utilisés. La portée de la clause where s'étend à toutes les protections. En revanche, la portée d'une expression let n'est que la clause de fonction actuelle et la garde, le cas échéant.

Aide-mémoire Haskell

Le Haskell Wiki est très détaillé et fournit divers cas mais il utilise des exemples hypothétiques. Je trouve ses explications trop brèves pour un débutant.

Avantages de Let :

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

Control.Monad.State

ne fonctionnera pas, car où fait référence au modèle correspondant à f =, où aucun x n'est dans la portée. En revanche, si vous aviez commencé avec let, vous n'auriez aucun problème.

Wiki Haskell sur les avantages de Let

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

Avantages de 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)

Déclaration vs Expression

Le wiki Haskell mentionne que la clause Where est déclarative tandis que l'expression Let est expressif. Mis à part le style, comment fonctionnent-ils différemment?

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. Dans le premier exemple, pourquoi Soit dans la portée mais n'est pas ?
  2. Est-il possible d'appliquer au premier exemple?
  3. Certains peuvent-ils l'appliquer à des exemples réels où les variables représentent des expressions réelles?
  4. Y a-t-il une règle générale à suivre pour les utiliser?

Mise à jour

Pour ceux qui viendront par ce fil plus tard, j'ai trouvé la meilleure explication ici: " A Gentle Introduction to Haskell ".

Soit Expressions.

Les expressions let de Haskell sont utiles chaque fois qu'un ensemble de liaisons imbriquées est requis. À titre d'exemple simple, considérez:

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

L'ensemble des liaisons créées par une expression let est mutuellement récursif et les liaisons de modèle sont traitées comme des modèles paresseux (c'est-à-dire qu'ils portent un ~ implicite). Les seuls types de déclarations autorisés sont les signatures de type, les liaisons de fonctions et les liaisons de modèles.

Où les clauses.

Parfois, il est pratique d'étendre les liaisons sur plusieurs équations protégées, ce qui nécessite une clause where:

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

Notez que cela ne peut pas être fait avec une expression let, qui ne couvre que l'expression qu'elle contient. Une clause where n'est autorisée qu'au niveau supérieur d'un ensemble d'équations ou d'expressions de casse. Les mêmes propriétés et contraintes sur les liaisons dans les expressions let s'appliquent à celles des clauses where. Ces deux formes de portée imbriquée semblent très similaires, mais rappelez-vous qu'une expression let est une expression, alors qu'une clause where ne l'est pas - elle fait partie de la syntaxe des déclarations de fonction et des expressions case.

105
user295190

1: Le problème dans l'exemple

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

est le paramètre x. Les éléments de la clause where ne peuvent faire référence qu'aux paramètres de la fonction f (il n'y en a pas) et aux éléments des étendues externes.

2: Pour utiliser un where dans le premier exemple, vous pouvez introduire une deuxième fonction nommée qui prend le x comme paramètre, comme ceci:

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

ou comme ça:

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

3: Voici un exemple complet sans le ...:

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: Quand utiliser let ou where est une question de goût. J'utilise let pour souligner un calcul (en le déplaçant vers l'avant) et where pour souligner le flux du programme (en déplaçant le calcul vers l'arrière).

35
antonakos

Bien qu'il y ait la différence technique en ce qui concerne les gardes que l'éphémient a souligné, il y a aussi une différence conceptuelle entre le fait de vouloir mettre la formule principale à l'avance avec des variables supplémentaires définies ci-dessous (where) ou si vous voulez définir tout à l'avance et mettez la formule ci-dessous (let). Chaque style a une emphase différente et vous les voyez tous les deux utilisés dans les articles de mathématiques, les manuels, etc. En général, les variables qui ne sont pas suffisamment intuitives pour que la formule n'a pas de sens sans elles devraient être définies ci-dessus; les variables intuitives en raison du contexte ou de leurs noms doivent être définies ci-dessous. Par exemple, dans l'exemple hasVowel de l'éphémient, la signification de vowels est évidente et il n'est donc pas nécessaire de la définir au-dessus de son utilisation (sans tenir compte du fait que let ne fonctionnerait pas à cause du gardien).

24
gdj

Légal:

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

Non légal:

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

Légal:

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

Non légal: (contrairement à ML)

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

J'ai trouvé cet exemple de LYHFGG utile:

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

let est une expression, vous pouvez donc mettre un let n'importe où (!) où les expressions peuvent aller.

En d'autres termes, dans l'exemple ci-dessus, il n'est pas possible d'utiliser where pour remplacer simplement let (sans peut-être en utilisant une expression case plus verbeuse combinée avec where).

3
jhegedus