# An endless list makes the REPL work for a long time and this is intentional (updated title)

**URL:** <https://forum.canton.network/t/an-endless-list-makes-the-repl-work-for-a-long-time-and-this-is-intentional-updated-title/2116>\
**Category:** App Development\
**Tags:** daml, repl, evaluation\
**Created:** [February 18, 2021, 5:30pm UTC](https://forum.canton.network/t/an-endless-list-makes-the-repl-work-for-a-long-time-and-this-is-intentional-updated-title/2116 "2021-02-18T17:30:40Z")\
**Posts on this page:** 12\
**Page:** 1

<div class="post-metadata">

**Author:** ![gyorgybalazsi](https://yyz2.discourse-cdn.com/flex034/user_avatar/forum.canton.network/gyorgybalazsi/32/684_2.png) [@gyorgybalazsi](https://forum.canton.network/u/gyorgybalazsi)\
**Post date:** [February 18, 2021, 5:30pm UTC](https://forum.canton.network/t/an-endless-list-makes-the-repl-work-for-a-long-time-and-this-is-intentional-updated-title/2116/1 "2021-02-18T17:30:40Z")

</div>

In Haskell, because of lazy evaluation, this is a legitimate expression:

![image](https://canada1.discourse-cdn.com/flex028/uploads/daml/original/2X/b/b0f6c922352be8a3a1583ddbd0b70af622a6b952.png)

Daml doesn’t have lazy evaluation, as I was told recently. The same expression sends the REPL into an infinite loop:

![image](https://canada1.discourse-cdn.com/flex028/uploads/daml/original/2X/6/66390de8577aa61ceeea549c28548a0e584a7307.png)

Is that intentional? (My guess is it isn’t.)

BTW, why is it that Daml doesn’t have lazy evaluation?

---

<div class="post-metadata">

**Author:** ![Stephen](https://yyz2.discourse-cdn.com/flex034/user_avatar/forum.canton.network/stephen/32/102_2.png) [@Stephen](https://forum.canton.network/u/Stephen)\
**Post date:** [February 18, 2021, 5:39pm UTC](https://forum.canton.network/t/an-endless-list-makes-the-repl-work-for-a-long-time-and-this-is-intentional-updated-title/2116/2 "2021-02-18T17:39:49Z")

</div>

> [@gyorgybalazsi](#):
>
> Is that intentional? (My guess is it isn’t.)

It is.

> [@gyorgybalazsi](#):
>
> BTW, why is it that Daml doesn’t have lazy evaluation?

I think the motives have changed over time, but nowadays I would say that proving properties of programs is easier if you do not have to cope with “actually nonterminating but kind of not if you really think about it” constructs like your sample. Additionally, all contract data must be strict, so the usefulness of lazy evaluation would be limited to ephemeral data structures, if it was supported at all.

---

<div class="post-metadata">

**Author:** ![gyorgybalazsi](https://yyz2.discourse-cdn.com/flex034/user_avatar/forum.canton.network/gyorgybalazsi/32/684_2.png) [@gyorgybalazsi](https://forum.canton.network/u/gyorgybalazsi)\
**Post date:** [February 18, 2021, 5:46pm UTC](https://forum.canton.network/t/an-endless-list-makes-the-repl-work-for-a-long-time-and-this-is-intentional-updated-title/2116/3 "2021-02-18T17:46:44Z")

</div>

I understand the second part, but not why it is desirable to send the REPL into an infinite loop instead of throwing an error.

---

<div class="post-metadata">

**Author:** ![cocreature](https://yyz2.discourse-cdn.com/flex034/user_avatar/forum.canton.network/cocreature/32/36_2.png) [@cocreature](https://forum.canton.network/u/cocreature)\
**Post date:** [February 18, 2021, 5:50pm UTC](https://forum.canton.network/t/an-endless-list-makes-the-repl-work-for-a-long-time-and-this-is-intentional-updated-title/2116/4 "2021-02-18T17:50:36Z")

</div>

> [@gyorgybalazsi](#):
>
> why it is desirable to send the REPL into an infinite loop instead of throwing an error.

This is less of a deliberate decision and more a consequence of the fact that we make no attempts at checking for termination. In general, this is of course undecidable (halting problem and all that). You could detect some specific cases like `[1..]` but so far it hasn’t seemed worth the effort and arguably might lead to a false sense of security where users expect that we always detect this.

---

<div class="post-metadata">

**Author:** ![gyorgybalazsi](https://yyz2.discourse-cdn.com/flex034/user_avatar/forum.canton.network/gyorgybalazsi/32/684_2.png) [@gyorgybalazsi](https://forum.canton.network/u/gyorgybalazsi)\
**Post date:** [February 18, 2021, 5:51pm UTC](https://forum.canton.network/t/an-endless-list-makes-the-repl-work-for-a-long-time-and-this-is-intentional-updated-title/2116/5 "2021-02-18T17:51:59Z")

</div>

Ok, understood, thank you.

---

<div class="post-metadata">

**Author:** ![cocreature](https://yyz2.discourse-cdn.com/flex034/user_avatar/forum.canton.network/cocreature/32/36_2.png) [@cocreature](https://forum.canton.network/u/cocreature)\
**Post date:** [February 18, 2021, 6:54pm UTC](https://forum.canton.network/t/an-endless-list-makes-the-repl-work-for-a-long-time-and-this-is-intentional-updated-title/2116/6 "2021-02-18T18:54:27Z")

</div>

> [@gyorgybalazsi](#):
>
> BTW, why is it that Daml doesn’t have lazy evaluation?

In addition to what @Stephen already mentioned, I think there are two reasons that still apply:

1. A lot more programmers are used to strict evaluation. The barrier of entry for learning Daml is already relatively high so matching the evaluation model people are used to has some advantages in that regard. It does cause confusion for people coming from Haskell but at least at this stage, that’s not how most people come towards Daml.
2. Implementing efficient runtimes/interpreters for lazy languages is usually significantly more complex and much less well explored than doing the same for strict languages. Outside of the Haskell runtime, there are relatively few examples that do this well. Interpreters for strict languages on the other hand exist in countless numbers and even implementing a reasonably efficient one is still a relatively well-explored field.

---

<div class="post-metadata">

**Author:** ![gyorgybalazsi](https://yyz2.discourse-cdn.com/flex034/user_avatar/forum.canton.network/gyorgybalazsi/32/684_2.png) [@gyorgybalazsi](https://forum.canton.network/u/gyorgybalazsi)\
**Post date:** [February 18, 2021, 7:25pm UTC](https://forum.canton.network/t/an-endless-list-makes-the-repl-work-for-a-long-time-and-this-is-intentional-updated-title/2116/7 "2021-02-18T19:25:56Z")

</div>

Yes, sounds reasonable, thank you

---

<div class="post-metadata">

**Author:** ![gyorgybalazsi](https://yyz2.discourse-cdn.com/flex034/user_avatar/forum.canton.network/gyorgybalazsi/32/684_2.png) [@gyorgybalazsi](https://forum.canton.network/u/gyorgybalazsi)\
**Post date:** [February 18, 2021, 7:36pm UTC](https://forum.canton.network/t/an-endless-list-makes-the-repl-work-for-a-long-time-and-this-is-intentional-updated-title/2116/8 "2021-02-18T19:36:49Z")

</div>

Just one more question, going back to my starting point.

The signature of the `take` function is like this:

[take](https://docs.daml.com/daml/stdlib/Prelude.html#function-da-internal-prelude-take-38395)

: [Int](https://docs.daml.com/daml/stdlib/Prelude.html#type-ghc-types-int-68728) → [a] → [a]

Take the first `n` elements of a list.

This means, the REPL typechecks the `[1..]` expression as a list when interpreting the `take 3 [1..]` expression, is that correct given the fact that without lazy evaluation the `[1..]` part doesn’t make any sense?

---

<div class="post-metadata">

**Author:** ![cocreature](https://yyz2.discourse-cdn.com/flex034/user_avatar/forum.canton.network/cocreature/32/36_2.png) [@cocreature](https://forum.canton.network/u/cocreature)\
**Post date:** [February 18, 2021, 7:38pm UTC](https://forum.canton.network/t/an-endless-list-makes-the-repl-work-for-a-long-time-and-this-is-intentional-updated-title/2116/9 "2021-02-18T19:38:04Z")

</div>

Typechecking is a static property independent of execution. `[1..]` is perfectly well typed as `[Int]`.

---

<div class="post-metadata">

**Author:** ![bernhard](https://yyz2.discourse-cdn.com/flex034/user_avatar/forum.canton.network/bernhard/32/356_2.png) [@bernhard](https://forum.canton.network/u/bernhard)\
**Post date:** [February 19, 2021, 8:31am UTC](https://forum.canton.network/t/an-endless-list-makes-the-repl-work-for-a-long-time-and-this-is-intentional-updated-title/2116/10 "2021-02-19T08:31:13Z")

</div>

Ranges like `[1..]`, `[1,3,..]`, `[1..5]`, `[1,3..11]` are just syntactic sugar for [`enumFrom`](https://docs.daml.com/daml/stdlib/Prelude.html#function-ghc-enum-enumfrom-34764), `enumFromThen`, `enumFromTo`, and `enumFromThenTo`, respectively.

So `take 3 [1..]` is the same as `take 3 (enumFrom 1)`. `enumFrom : a -> [a]` so `[1..]` does indeed have type `[Int]`.

If you look at [how this is implemented](https://github.com/digital-asset/daml/blob/a925f0174c36986dc0609c8506444148d031fcdc/compiler/damlc/daml-prim-src/GHC/Enum.daml#L152-L178), you see that `[1..] == enumFrom 1 == enumFromTo 1 maxBound == eftInt 1 maxBound` where

```haskell
eftInt : Int -> Int -> [Int]
-- [x1..x2]
eftInt x y | x > y = []
           | x == y = [x]
           | otherwise = x :: eftInt (x + 1) y

```

```haskell
    maxBound = 0x7FFFFFFFFFFFFFFF

```

In other words, your expressions will terminate. It’ll just take a while. You recurse roughly 10^19 times.

---

<div class="post-metadata">

**Author:** ![gyorgybalazsi](https://yyz2.discourse-cdn.com/flex034/user_avatar/forum.canton.network/gyorgybalazsi/32/684_2.png) [@gyorgybalazsi](https://forum.canton.network/u/gyorgybalazsi)\
**Post date:** [February 19, 2021, 8:35am UTC](https://forum.canton.network/t/an-endless-list-makes-the-repl-work-for-a-long-time-and-this-is-intentional-updated-title/2116/11 "2021-02-19T08:35:04Z")

</div>

Oh yes, thank you. In this case the correct description is not that the REPL gets into an infinite loop, but that it will work for a long time.

Now I understand it.

---

<div class="post-metadata">

**Author:** ![anthony](https://yyz2.discourse-cdn.com/flex034/user_avatar/forum.canton.network/anthony/32/419_2.png) [@anthony](https://forum.canton.network/u/anthony)\
**Post date:** [February 19, 2021, 2:15pm UTC](https://forum.canton.network/t/an-endless-list-makes-the-repl-work-for-a-long-time-and-this-is-intentional-updated-title/2116/12 "2021-02-19T14:15:04Z")

</div>

> [@bernhard](#):
>
> In other words, your expressions will terminate. It’ll just take a while. You recurse roughly 10^19 times.

Did it pretty quickly on my machine (in that the heap blew up before it terminated). More interestingly it:

1. Ran
2. Appeared to return to console without any output (but started ignoring any input)
3. Obvious a process was still running because my laptop fan was going wild
4. Then the heap blew up
5. Then gave me a terminal where I couldn’t actually do anything anymore

```haskell
$ daml repl
daml> take 3 [1..]
daml> Uncaught error from thread [Repl-akka.actor.default-dispatcher-11]: Java heap space, shutting down JVM since 'akka.jvm-exit-on-fatal-error' is enabled for ActorSystem[Repl]
java.lang.OutOfMemoryError: Java heap space
	at java.base/java.util.Arrays.copyOf(Arrays.java:3690)
	at java.base/java.util.ArrayList.grow(ArrayList.java:237)
	at java.base/java.util.ArrayList.grow(ArrayList.java:242)
	at java.base/java.util.ArrayList.add(ArrayList.java:485)
	at java.base/java.util.ArrayList.add(ArrayList.java:498)
	at com.daml.lf.speedy.Speedy$KPushTo.execute(Speedy.scala:1130)
	at com.daml.lf.speedy.Speedy$Machine.run(Speedy.scala:391)
	at com.daml.lf.engine.script.Runner.stepToValue$1(Runner.scala:465)
	at com.daml.lf.engine.script.Runner.$anonfun$runWithClients$64(Runner.scala:829)
	at com.daml.lf.engine.script.Runner$$Lambda$903/0x000000080179f040.apply(Unknown Source)
	at scala.concurrent.Future.$anonfun$flatMap$1(Future.scala:307)
	at scala.concurrent.Future$$Lambda$273/0x0000000801409840.apply(Unknown Source)
	at scala.concurrent.impl.Promise.$anonfun$transformWith$1(Promise.scala:41)
	at scala.concurrent.impl.Promise$$Lambda$274/0x000000080140a840.apply(Unknown Source)
	at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:64)
	at akka.dispatch.BatchingExecutor$AbstractBatch.processBatch(BatchingExecutor.scala:56)
	at akka.dispatch.BatchingExecutor$BlockableBatch.$anonfun$run$1(BatchingExecutor.scala:93)
	at akka.dispatch.BatchingExecutor$BlockableBatch$$Lambda$271/0x00000008013f0c40.apply$mcV$sp(Unknown Source)
	at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
	at scala.concurrent.BlockContext$.withBlockContext(BlockContext.scala:85)
	at akka.dispatch.BatchingExecutor$BlockableBatch.run(BatchingExecutor.scala:93)
	at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:48)
	at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(ForkJoinExecutorConfigurator.scala:48)
	at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
	at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
	at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
	at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
	at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)

daml> take 3 [1..]
daml> take 3 [1,2,3,4]
daml> 

```

I’ll file a bug report. I think the REPL should either fail completely or give me back a usable terminal but not this. It’s deceptive in the output but the line `daml> Uncaught error...` actually was `daml> ` before the heap blew up and started printing to the terminal.

Edit: Opened an issue

> <https://github.com/digital-asset/daml/issues/8901>
>
> Inspired by \[György\](https://discuss.daml.com/t/an-endless-lit-makes-the-repl-wo…rk-for-a-long-time-and-this-is-intentional-updated-title/2116) and \[Bernhard's\](https://discuss.daml.com/t/an-endless-lit-makes-the-repl-work-for-a-long-time-and-this-is-intentional-updated-title/2116/10?u=anthony) posts on non-terminating/long running functions in Daml in general and specifically in the REPL I decided to see what would actually happen if I ran György's code.
> 
> Expected behavior: REPL should either fail completely or give me back a usable terminal
> Actual behavior: REPL fails and is left in an odd state where it gives me a terminal that doesn't accept input
> 
> Steps to repeat:
> 
> 1. Run \`take 3 \[1..\]\` or any long running function
> 1. REPL appears to return to console without any output (but started ignoring any input)
> 1. Obvious a process was still running because my laptop fan was going wild
> 1. Get \`java.lang.OutOfMemoryError: Java heap space\` error
> 1. Get a terminal where I couldn't actually run any commands
> 
> RE point 2 above, it's deceptive in the output below but the line \`daml\> Uncaught error...\` actually was \`daml\> \` before the heap blew up and started printing to the terminal.
> 
> \`\`\`
> $ daml repl
> daml\> take 3 \[1..\]
> daml\> Uncaught error from thread \[Repl-akka.actor.default-dispatcher-11\]: Java heap space, shutting down JVM since 'akka.jvm-exit-on-fatal-error' is enabled for ActorSystem\[Repl\]
> java.lang.OutOfMemoryError: Java heap space
> at java.base/java.util.Arrays.copyOf(Arrays.java:3690)
> at java.base/java.util.ArrayList.grow(ArrayList.java:237)
> at java.base/java.util.ArrayList.grow(ArrayList.java:242)
> at java.base/java.util.ArrayList.add(ArrayList.java:485)
> at java.base/java.util.ArrayList.add(ArrayList.java:498)
> at com.daml.lf.speedy.Speedy$KPushTo.execute(Speedy.scala:1130)
> at com.daml.lf.speedy.Speedy$Machine.run(Speedy.scala:391)
> at com.daml.lf.engine.script.Runner.stepToValue$1(Runner.scala:465)
> at com.daml.lf.engine.script.Runner.$anonfun$runWithClients$64(Runner.scala:829)
> at com.daml.lf.engine.script.Runner$$Lambda$903/0x000000080179f040.apply(Unknown Source)
> at scala.concurrent.Future.$anonfun$flatMap$1(Future.scala:307)
> at scala.concurrent.Future$$Lambda$273/0x0000000801409840.apply(Unknown Source)
> at scala.concurrent.impl.Promise.$anonfun$transformWith$1(Promise.scala:41)
> at scala.concurrent.impl.Promise$$Lambda$274/0x000000080140a840.apply(Unknown Source)
> at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:64)
> at akka.dispatch.BatchingExecutor$AbstractBatch.processBatch(BatchingExecutor.scala:56)
> at akka.dispatch.BatchingExecutor$BlockableBatch.$anonfun$run$1(BatchingExecutor.scala:93)
> at akka.dispatch.BatchingExecutor$BlockableBatch$$Lambda$271/0x00000008013f0c40.apply$mcV$sp(Unknown Source)
> at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
> at scala.concurrent.BlockContext$.withBlockContext(BlockContext.scala:85)
> at akka.dispatch.BatchingExecutor$BlockableBatch.run(BatchingExecutor.scala:93)
> at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:48)
> at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(ForkJoinExecutorConfigurator.scala:48)
> at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
> at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
> at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
> at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
> at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
> 
> daml\> take 3 \[1..\]
> daml\> take 3 \[1,2,3,4\]
> daml\> 
> \`\`\`
