From dd20c743a20dbf7ab7c4c30f9498845116bffd52 Mon Sep 17 00:00:00 2001 From: wojpok Date: Mon, 3 Nov 2025 15:46:55 +0100 Subject: [PATCH 1/2] First class lazy evaluation --- lib/Lazy.fram | 65 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 lib/Lazy.fram diff --git a/lib/Lazy.fram b/lib/Lazy.fram new file mode 100644 index 00000000..9201fd54 --- /dev/null +++ b/lib/Lazy.fram @@ -0,0 +1,65 @@ +{# This file is part of DBL, released under the MIT license. + # See LICENSE for details. + #} + +## # Fist-class lazy values + +{## + This module provides first-class lazy values, also known assuspensions, + representing deferred computations evaluated on demand. + + A suspension encapsulates a computation that is executed only when its + result is first required. Once evaluated, the resulting value is memoized, + guaranteeing that subsequent forcings complete in constant time. + + To preserve determinism, only effect-free thunks may be used as deferred + computations. + ##} + +# Mutability utilities +data RefP = RefP of {Ref : type -> type} + +let RefP {Ref} = (extern dbl_abstrType : Unit -> RefP) () + +let ref {type X} = (extern dbl_ref : X -> Ref X) +method get {type X} = (extern dbl_refGet : Ref X -> X) +method set {type X} = (extern dbl_refSet : Ref X -> X -> Unit) + +# Internal representation of lazy state +data LazyState A = + | Thunk of (Unit ->[] A) + | Lock + | Done of A + +{## + Represents a suspension. + ##} +abstr data Lazy X = Lazy of Ref (LazyState X) + +{## Creates new suspension from a given computation. ##} +pub let lazy {type X} (f : Unit ->[] X) = Lazy (ref (Thunk f)) + +{## Initialized a lazy with a value directly. ##} +pub let pureLazy {type X} (v : X) = Lazy (ref (Done v)) + +{## + Forces the evaluation of a suspension. + + If the suspension has not yet been evaluated, its computation is executed + and the resulting value is stored. If the suspension is currently under + evaluation, a runtime error is raised to prevent re-entrant forcing. + ##} +pub method force (Lazy ref) = + match ref.get with + | Done x => x + | Lock => runtimeError "Forcing lazy value during its evaluation" + | Thunk f => + ref.set Lock; + let val = f () in + ref.set (Done val); + val + end + +{## Creates new lazy value with mapped results. ##} +pub method map {X : type} (self : Lazy X) f = + lazy (fn _ => f self.force) From a907a4271e2b96cb86e246c5be6103f11224a960 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wojciech=20Pok=C3=B3j?= Date: Mon, 3 Nov 2025 20:33:24 +0100 Subject: [PATCH 2/2] Applied requested changes --- lib/Lazy.fram | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/lib/Lazy.fram b/lib/Lazy.fram index 9201fd54..9c5fa49a 100644 --- a/lib/Lazy.fram +++ b/lib/Lazy.fram @@ -2,10 +2,10 @@ # See LICENSE for details. #} -## # Fist-class lazy values +## # First-class lazy values {## - This module provides first-class lazy values, also known assuspensions, + This module provides first-class lazy values, also known as suspensions, representing deferred computations evaluated on demand. A suspension encapsulates a computation that is executed only when its @@ -19,11 +19,11 @@ # Mutability utilities data RefP = RefP of {Ref : type -> type} -let RefP {Ref} = (extern dbl_abstrType : Unit -> RefP) () +let RefP {Ref} = (extern dbl_abstrType : Unit ->[IO] RefP) () -let ref {type X} = (extern dbl_ref : X -> Ref X) -method get {type X} = (extern dbl_refGet : Ref X -> X) -method set {type X} = (extern dbl_refSet : Ref X -> X -> Unit) +let ref {type X} = (extern dbl_ref : X ->[] Ref X) +method get {type X} = (extern dbl_refGet : Ref X ->[] X) +method set {type X} = (extern dbl_refSet : Ref X -> X ->[] Unit) # Internal representation of lazy state data LazyState A = @@ -31,15 +31,13 @@ data LazyState A = | Lock | Done of A -{## - Represents a suspension. - ##} +{## Represents a suspension. ##} abstr data Lazy X = Lazy of Ref (LazyState X) {## Creates new suspension from a given computation. ##} pub let lazy {type X} (f : Unit ->[] X) = Lazy (ref (Thunk f)) -{## Initialized a lazy with a value directly. ##} +{## Initializes a lazy with a value directly. ##} pub let pureLazy {type X} (v : X) = Lazy (ref (Done v)) {##