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.
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.
In addition to what @Stephen already mentioned, I think there are two reasons that still apply:
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.
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.
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?
Did it pretty quickly on my machine (in that the heap blew up before it terminated). More interestingly it:
Ran
Appeared to return to console without any output (but started ignoring any input)
Obvious a process was still running because my laptop fan was going wild
Then the heap blew up
Then gave me a terminal where I couldn’t actually do anything anymore
$ 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.