PureScript is a Haskell like programming language with some features biaised towards its main compilation backend which is JavaScript.

Between them strictness by default, JavaScript primitive types and literals where rectangular brackets are for the type Array instead of List, and some other differences.

$ pulp repl PSCi, version 0.11.7 Type :? for help import Prelude > :t [1,2,3] Array Int > :t true Boolean > :t 1.5 Number > :t unit Unit -- ranges don't use brackets and are defined for Arrays and for Lists > import Data.Array ((..)) > :t 1..5 Array Int

There is a Data.Enum module that defines the finite sequences enumFromTo and enumFromThenTo but not the infinite ones enumFrom and enumFromThen, and looking for a solution I have encountered some problems.

The lists are defined in a non Prelude package called purescript-lists

There are strict list modules Data.List, Data.List.NonEmpty and lazy lists ones Data.List.Lazy and Data.List.Lazy.NonEmpty based on a Lazy type wrapper defined in purescript-lazy Data.Lazy module.

Trying to define a Lazy sequence with a stop point seems to work apparently without problems but, defining one on Int(s) without stop point, makes the stack overflow.

> import Data.List.Lazy > > :paste … f :: Int -> List Int … f x | x > 0 = x : f (x-1) … | otherwise = nil … > show $ take 3 $ f 9 "fromStrict ((Cons 9 (Cons 8 (Cons 7 Nil))))" > :paste … g :: Int -> List Int … g x = x : g (x+1) … > show $ take 3 $ g 0 /home/gabi64/lleng/purescript/soh/lazy2/.psci_modules/node_modules/$PSCI/index.js:13 var g = function (x) { ^ RangeError: Maximum call stack size exceeded >

It seems that strictness is playing a dirty trick here, evaluating the second cons parameter beforehand against our interest, but strict evaluation is in the nature of PureScript.

But if we use the cons function definition expression with the Data.Lazy defer function, instead of going with the (:) operator, the evaluation of the recursive case will be effectively deferred.

-- | cons as defined In Data.List.Lazy cons :: forall a. a -> List a -> List a cons x xs = List $ defer \_ -> Cons x xs -- Cons is a constructor of the type Step -- defining our function the same way > import Data.Lazy > h x = List $ defer \_ -> Cons x $ h (x+1) > show $ take 3 $ h 0 "fromStrict ((Cons 0 (Cons 1 (Cons 2 Nil))))"

Now it works!

Let's try defining enumFrom

> import Data.Maybe > import Data.Enum > :paste … enumFrom :: forall a. Enum a => a -> List a … enumFrom x = List $ defer \_ -> case succ x of … Just x' -> Cons x $ enumFrom x' … Nothing -> Cons x nil … > show $ take 3 $ enumFrom 0 "fromStrict ((Cons 0 (Cons 1 (Cons 2 Nil))))" > show $ take 3 $ enumFrom 'A' "fromStrict ((Cons 'A' (Cons 'B' (Cons 'C' Nil))))"

Let's try defining enumFromThen that corresponds in haskell to a stepped Enum sequence specifying the second value:

(it requires the class BoundedEnum that defines toEnum and fromEnum but there is no instance for Ints)

> import Data.Lazy > import Data.Maybe > import Data.Enum > import Data.List.Lazy > :paste … enumFromThen :: forall a. BoundedEnum a => a -> a -> List a … enumFromThen x y = List $ defer \_ -> … let step = fromEnum y - fromEnum x … mbZ = toEnum (fromEnum y + step) :: Maybe a … in case mbZ of … Just z -> Cons x $ enumFromThen y z … Nothing -> Cons x nil … > show $ take 3 $ enumFromThen 'E' 'C' "fromStrict ((Cons 'E' (Cons 'C' (Cons 'A' Nil))))"

To add an instance for Ints, because orphan instances are disallowed the only way is to define it in the EnumBounded class module making a custom version.

-- added to a local copy of Data.Enum instance boundedEnumInt :: BoundedEnum Int where cardinality = Cardinality (top - bottom + 1) -- overflowed but unused unless you use Ints as components of compound BoundedEnum instances fromEnum = id toEnum = Just

Now the package manager bower complaints that my local "Data.Enum" module name collides with the installed purescript-enums one.

bower uninstall purescript-enums

The psc-package packaging system is a better option as it is based, à la stackage, on sets of libraries that are guaranteed compatible.

Adding a psc-package project file.

git clone https://github.com/purescript/psc-package cd psc-package stack install cd path/to/my-project psc-package init # interactively build the project file # to start a new psc-package project: pulp --psc-package init

The project file psc-package.json after adding some dependencies (with psc-package install pkgName ):

{ "name": "lazy4", "set": "psc-0.11.7", "source": "https://github.com/purescript/package-sets.git", "depends": [ "monoid", "foldable-traversable", "console", "prelude", "lists", "strings", "lazy" ] }

Testing for Int sequences:

module Main where import Prelude import Control.Monad.Eff (Eff) import Control.Monad.Eff.Console (CONSOLE, log) import Data.List.Lazy (List(..), nil, take, Step(Cons)) import Data.Lazy (defer) import Data.Enum (class BoundedEnum, class Enum, fromEnum, succ, toEnum) import Data.Maybe (Maybe(..)) enumFrom :: forall a. Enum a => a -> List a enumFrom x = List $ defer \_ -> case succ x of Just x' -> Cons x $ enumFrom x' Nothing -> Cons x nil enumFromThen :: forall a. BoundedEnum a => a -> a -> List a enumFromThen x y = List $ defer \_ -> let step = fromEnum y - fromEnum x mbZ = toEnum (fromEnum y + step) :: Maybe a in case mbZ of Just z -> Cons x $ enumFromThen y z Nothing -> Cons x nil main :: forall e. Eff (console :: CONSOLE | e) Unit main = log $ show $ take 5 $ enumFromThen 1 3

Output:

$ pulp --psc-package run Compiling Data.Enum Compiling Main * Build successful. fromStrict ((Cons 1 (Cons 3 (Cons 5 (Cons 7 (Cons 9 Nil))))))