2017-04-21 1 views
3

J'ai vu cet extrait de Haskell en this answer par proud haskeller sur Meta PPCG: "attends, je peux le faire à Scala"Pourquoi une liste paresseuse récursive souffle-t-elle la pile dans Scala?

x=2:x 

Je pensais que, J'ai donc essayé:

lazy val x: List[Int] = 2 :: x 

Il a compilé et imprimé ma console une belle x: List[Int] = <lazy>. Mais chacune de ces lignes se traduit par une StackOverflowException:

x take 1 
x.head 
x(1) 
x 

Basé sur le dernier, il semble que toute tentative d'utiliser x souffle la pile essayant de calculer x (ça ou un débordement de pile se passe-t essayer d'imprimer dans la console). En quoi la paresse de Scala est-elle différente de la paresse d'Haskell dans cet exemple? Est-ce une caractéristique de lazy val de Scala ou est-ce que la classe List nécessite simplement une queue complète?

Répondre

10

Ce que vous voulez est def x: Stream[Int] = 2 #:: x. Cela produit un immutable.Stream[Int]. Une variable paresseuse est évaluée seulement si nécessaire, mais elle est entièrement évaluée. A Stream, d'autre part, est une collection de valeurs paresseuses. Chaque élément est évalué uniquement en cas de besoin, mais la collection entière peut ne jamais être évaluée, ce qui explique pourquoi elle peut être infinie.

+0

Pas tout à fait, vous avez besoin du type de retour pour compiler, mais toujours agréable. Avec vals paresseux, c'est paresseux val x: Stream [Int] = 2 # :: x'. –

+0

@BrianMcCutchon, c'est vrai. Occupé composer le 2ème paragraphe et raté ça. Je dirais, cependant, qu'il est inutile de rendre le 'Stream' val paresseux. Si c'est un 'val' alors les résultats (éléments évalués) sont mémoized. Si c'est un 'var' ils ne le sont pas. Faire un «valse paresseux» ne vous achète pas beaucoup. – jwvh

+0

Oups. Une autre faute de frappe essayant de clarifier la différence entre le flux 'val', qui est mémoized, et le flux' def' (pas 'var'), qui n'est pas mémoized. – jwvh

1

Eh bien, il semble que je l'ai compris tout en formulant la question. Il semble être plus un problème avec List qu'avec lazy val. Pour l'essayer, j'ai fait une implémentation simple LazyList:

class LazyList(h: Int, t: => LazyList) { 
    val head = h 
    lazy val tail = t 
} 

alors je peux faire:

lazy val x: LazyList = new LazyList(1, x) 
x.head // 1 
x.tail.tail.tail.head // 1 

Ainsi, la paresse de Scala est vraiment paresseux après tout, si vous faites tout paresseux, au moins.

+0

Notez que la même chose se serait exactement produite si vous utilisiez une liste stricte dans Haskell. –

+0

@ JörgWMittag Intéressant. Je connais très peu Haskell, mais je suis surpris qu'il ait un type de liste strict. –