@effekt-lang/effekt 0.17.0 → 0.19.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/effekt CHANGED
Binary file
@@ -34,7 +34,7 @@ def emptyBuffer[T](capacity: Int): Buffer[T] at {global} = {
34
34
  }
35
35
  def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
36
36
  // TODO allocate buffer (and array) into a region r.
37
- val contents = emptyArray[T](initialCapacity)
37
+ val contents = array::allocate[T](initialCapacity)
38
38
  var head in global = 0
39
39
  var tail in global = 0
40
40
 
@@ -51,7 +51,7 @@ def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
51
51
  def read() = {
52
52
  if (buffer.empty?) None()
53
53
  else {
54
- val result: T = contents.remove(head).getOrElse { <> };
54
+ val result: T = contents.unsafeGet(head);
55
55
  head = mod(head + 1, initialCapacity)
56
56
  Some(result)
57
57
  }
@@ -59,7 +59,7 @@ def arrayBuffer[T](initialCapacity: Int): Buffer[T] at {global} = {
59
59
  def write(el: T) = {
60
60
  if (buffer.full?) <> // raise(BufferOverflow())
61
61
 
62
- contents.put(tail, el)
62
+ contents.unsafeSet(tail, el)
63
63
  tail = mod(tail + 1, initialCapacity)
64
64
  }
65
65
  }
@@ -6,7 +6,7 @@ module bytearray
6
6
  extern type ByteArray
7
7
  // = llvm "%Pos"
8
8
  // = js "Uint8Array"
9
-
9
+ // = chez "bytevector"
10
10
 
11
11
  /// Allocates a new bytearray with the given `size`, its values are undefined.
12
12
  extern global def allocate(size: Int): ByteArray =
@@ -15,6 +15,7 @@ extern global def allocate(size: Int): ByteArray =
15
15
  %arr = call %Pos @c_bytearray_new(%Int ${size})
16
16
  ret %Pos %arr
17
17
  """
18
+ chez "(make-bytevector ${size})"
18
19
 
19
20
  extern pure def size(arr: ByteArray): Int =
20
21
  js "${arr}.length"
@@ -22,6 +23,7 @@ extern pure def size(arr: ByteArray): Int =
22
23
  %size = call %Int @c_bytearray_size(%Pos ${arr})
23
24
  ret %Int %size
24
25
  """
26
+ chez "(bytevector-length ${arr})"
25
27
 
26
28
  extern global def unsafeGet(arr: ByteArray, index: Int): Byte =
27
29
  js "(${arr})[${index}]"
@@ -29,6 +31,7 @@ extern global def unsafeGet(arr: ByteArray, index: Int): Byte =
29
31
  %byte = call %Byte @c_bytearray_get(%Pos ${arr}, %Int ${index})
30
32
  ret %Byte %byte
31
33
  """
34
+ chez "(bytevector-u8-ref ${arr} ${index})"
32
35
 
33
36
  extern global def unsafeSet(arr: ByteArray, index: Int, value: Byte): Unit =
34
37
  js "bytearray$set(${arr}, ${index}, ${value})"
@@ -36,6 +39,7 @@ extern global def unsafeSet(arr: ByteArray, index: Int, value: Byte): Unit =
36
39
  %z = call %Pos @c_bytearray_set(%Pos ${arr}, %Int ${index}, %Byte ${value})
37
40
  ret %Pos %z
38
41
  """
42
+ chez "(bytevector-u8-set! ${arr} ${index} ${value})"
39
43
 
40
44
  def resize(source: ByteArray, size: Int): ByteArray = {
41
45
  val target = allocate(size)
@@ -75,6 +79,7 @@ extern pure def fromString(str: String): ByteArray =
75
79
  llvm """
76
80
  ret %Pos ${str}
77
81
  """
82
+ chez "(string->utf8 ${str})"
78
83
 
79
84
  extern pure def toString(arr: ByteArray): String =
80
85
  js "(new TextDecoder('utf-8').decode(${arr}))"
@@ -82,10 +87,68 @@ extern pure def toString(arr: ByteArray): String =
82
87
  llvm """
83
88
  ret %Pos ${arr}
84
89
  """
90
+ chez "(utf8->string ${arr})"
85
91
 
86
92
  extern js """
87
93
  function bytearray$set(bytes, index, value) {
88
94
  bytes[index] = value;
89
95
  return $effekt.unit;
90
96
  }
97
+
98
+ function bytearray$compare(arr1, arr2) {
99
+ const len = Math.min(arr1.length, arr2.length);
100
+
101
+ for (let i = 0; i < len; i++) {
102
+ if (arr1[i] !== arr2[i]) {
103
+ return arr1[i] < arr2[i] ? -1 : 1;
104
+ }
105
+ }
106
+
107
+ if (arr1.length !== arr2.length) {
108
+ return arr1.length < arr2.length ? -1 : 1;
109
+ } else {
110
+ return 0;
111
+ }
112
+ }
113
+ """
114
+
115
+ extern chez """
116
+ (define (bytearray$compare b1 b2)
117
+ (let ([len1 (bytevector-length b1)]
118
+ [len2 (bytevector-length b2)]
119
+ [minlen (min (bytevector-length b1) (bytevector-length b2))])
120
+ (let loop ([i 0])
121
+ (cond
122
+ [(= i minlen)
123
+ (cond [(< len1 len2) -1]
124
+ [(> len1 len2) 1]
125
+ [else 0])]
126
+ [(< (bytevector-u8-ref b1 i) (bytevector-u8-ref b2 i)) -1]
127
+ [(> (bytevector-u8-ref b1 i) (bytevector-u8-ref b2 i)) 1]
128
+ [else (loop (+ i 1))]))))
91
129
  """
130
+
131
+ extern pure def compareByteArrayImpl(b1: ByteArray, b2: ByteArray): Int =
132
+ js "bytearray$compare(${b1}, ${b2})"
133
+ llvm """
134
+ %x = call %Int @c_bytearray_compare(%Pos ${b1}, %Pos ${b2})
135
+ ret %Int %x
136
+ """
137
+ chez "(bytearray$compare ${b1} ${b2})"
138
+
139
+ def compareByteArray(b1: ByteArray, b2: ByteArray): Ordering = {
140
+ val ret = compareByteArrayImpl(b1, b2)
141
+ if (ret == 0) {
142
+ Equal()
143
+ } else if (ret < 0) {
144
+ Less()
145
+ } else { // ret > 0
146
+ Greater()
147
+ }
148
+ }
149
+
150
+ def compareStringBytes(left: String, right: String): Ordering = {
151
+ val l = left.fromString
152
+ val r = right.fromString
153
+ compareByteArray(l, r)
154
+ }
@@ -90,6 +90,7 @@ extern pure def show(value: Char): String =
90
90
 
91
91
  extern pure def show(value: Byte): String =
92
92
  js "'' + ${value}"
93
+ chez "(string ${value})"
93
94
  llvm """
94
95
  %z = call %Pos @c_bytearray_show_Byte(i8 ${value})
95
96
  ret %Pos %z
@@ -166,17 +167,24 @@ type Ordering {
166
167
  Greater()
167
168
  }
168
169
 
170
+ def compareInt(n: Int, m: Int) =
171
+ if (n == m) Equal()
172
+ else if (n < m) Less()
173
+ else Greater()
174
+
169
175
  extern pure def genericCompareImpl[R](x: R, y: R): Int =
170
176
  js "$effekt.compare(${x}, ${y})"
171
177
 
172
178
  /// Compares two values of the same type, returning an `Ordering`.
173
179
  /// Only available on the JavaScript backend.
174
180
  def genericCompare[R](x: R, y: R): Ordering = {
175
- genericCompareImpl(x, y) match {
176
- case -1 => Less()
177
- case 0 => Equal()
178
- case 1 => Greater()
179
- case _ => <{ "Impossible: JS comparison returned invalid value!" }>
181
+ val ret = genericCompareImpl(x, y)
182
+ if (ret == 0) {
183
+ Equal()
184
+ } else if (ret < 0) {
185
+ Less()
186
+ } else { // ret > 0
187
+ Greater()
180
188
  }
181
189
  }
182
190
 
@@ -344,21 +352,25 @@ extern pure def cos(x: Double): Double =
344
352
  js "Math.cos(${x})"
345
353
  chez "(cos ${x})"
346
354
  vm "effekt::cos(Double)"
355
+ llvm "%z = call %Double @llvm.cos.f64(double ${x}) ret %Double %z"
347
356
 
348
357
  extern pure def sin(x: Double): Double =
349
358
  js "Math.sin(${x})"
350
359
  chez "(sin ${x})"
351
360
  vm "effekt::sin(Double)"
361
+ llvm "%z = call %Double @llvm.sin.f64(double ${x}) ret %Double %z"
352
362
 
353
363
  extern pure def atan(x: Double): Double =
354
364
  js "Math.atan(${x})"
355
365
  chez "(atan ${x})"
356
366
  vm "effekt::atan(Double)"
367
+ llvm "%z = call %Double @atan(double ${x}) ret %Double %z"
357
368
 
358
369
  extern pure def tan(x: Double): Double =
359
370
  js "Math.tan(${x})"
360
371
  chez "(tan ${x})"
361
372
  vm "effekt::tan(Double)"
373
+ llvm "%z = call %Double @tan(double ${x}) ret %Double %z"
362
374
 
363
375
  extern pure def sqrt(x: Double): Double =
364
376
  js "Math.sqrt(${x})"
@@ -381,15 +393,18 @@ extern pure def log(x: Double): Double =
381
393
  js "Math.log(${x})"
382
394
  chez "(log ${x})"
383
395
  vm "effekt::log(Double)"
396
+ llvm "%z = call %Double @llvm.log.f64(double ${x}) ret %Double %z"
384
397
 
385
398
  extern pure def log1p(x: Double): Double =
386
399
  js "Math.log1p(${x})"
387
400
  chez "(log (+ ${x} 1))"
401
+ llvm "%z = call %Double @log1p(double ${x}) ret %Double %z"
388
402
 
389
403
  extern pure def exp(x: Double): Double =
390
404
  js "Math.exp(${x})"
391
405
  chez "(exp ${x})"
392
406
  vm "effekt::exp(Double)"
407
+ llvm "%z = call %Double @llvm.exp.f64(double ${x}) ret %Double %z"
393
408
 
394
409
  def pow(base: Double, exponent: Int): Double = {
395
410
  def loop(base: Double, exponent: Int, acc: Double): Double = {
@@ -408,12 +423,14 @@ extern pure def pow(base: Double, exponent: Double): Double =
408
423
  js "Math.pow(${base}, ${exponent})"
409
424
  chez "(expt ${base} ${exponent})"
410
425
  vm "effekt::pow(Double, Double)"
426
+ llvm "%z = call %Double @llvm.pow.f64(double ${base}, double ${exponent}) ret %Double %z"
411
427
 
412
428
  // since we do not have "extern val", yet
413
429
  extern pure def _pi(): Double =
414
430
  js "Math.PI"
415
431
  chez "(* 4 (atan 1))"
416
432
  vm "effekt::pi()"
433
+ llvm "ret double 3.14159265358979323846264338327950288419716939937510582097494459"
417
434
 
418
435
  val PI: Double = _pi()
419
436
 
@@ -562,19 +579,19 @@ extern pure def infixGte(x: Double, y: Double): Bool =
562
579
  // TODO do we really need those? if yes, move to string.effekt
563
580
  extern pure def infixLt(x: String, y: String): Bool =
564
581
  js "(${x} < ${y})"
565
- chez "(< ${x} ${y})"
582
+ chez "(string<? ${x} ${y})"
566
583
 
567
584
  extern pure def infixLte(x: String, y: String): Bool =
568
585
  js "(${x} <= ${y})"
569
- chez "(<= ${x} ${y})"
586
+ chez "(string<=? ${x} ${y})"
570
587
 
571
588
  extern pure def infixGt(x: String, y: String): Bool =
572
589
  js "(${x} > ${y})"
573
- chez "(> ${x} ${y})"
590
+ chez "(string>? ${x} ${y})"
574
591
 
575
592
  extern pure def infixGte(x: String, y: String): Bool =
576
593
  js "(${x} >= ${y})"
577
- chez "(>= ${x} ${y})"
594
+ chez "(string>=? ${x} ${y})"
578
595
 
579
596
 
580
597
  // Boolean operations
@@ -638,10 +655,13 @@ extern pure def bitwiseXor(x: Int, y: Int): Int =
638
655
  extern pure def toByte(n: Int): Byte =
639
656
  js "${n}"
640
657
  llvm "%z = trunc %Int ${n} to %Byte ret %Byte %z"
658
+ chez "${n}"
641
659
 
642
660
  extern pure def toInt(n: Byte): Int =
643
661
  js "${n}"
644
662
  llvm "%z = zext %Byte ${n} to %Int ret %Int %z"
663
+ vm "effekt::toInt(Byte)"
664
+ chez "${n}"
645
665
 
646
666
 
647
667
  // Undefined and Null
@@ -657,11 +677,13 @@ extern pure def toInt(n: Byte): Int =
657
677
  extern pure def undefined[A](): A =
658
678
  js "undefined"
659
679
  chez "#f"
680
+ vm "effekt::undefined()"
660
681
 
661
682
  /// Is an FFI value undefined?
662
683
  extern pure def isUndefined[A](value: A): Bool =
663
684
  js "(${value} === undefined || ${value} === null)"
664
685
  chez "(eq? ${value} #f)"
686
+ vm "effekt::isUndefined[A](A)"
665
687
 
666
688
 
667
689
  // Tuples
@@ -57,14 +57,17 @@ def server(host: String, port: Int, handler: () => Unit / Socket at {io, async,
57
57
 
58
58
  namespace examples {
59
59
  def helloWorldApp(): Unit / Socket = {
60
- val request = do receive();
60
+ val request = do receive().toString;
61
61
 
62
- println("Received a request: " ++ request.toUTF8)
62
+ println("Received a request: " ++ request)
63
63
 
64
- if (request.toUTF8.startsWith("GET /")) {
65
- do send(fromUTF8("HTTP/1.1 200 OK\r\n\r\nHello from Effekt!"))
64
+ def respond(s: String): Unit / Socket =
65
+ do send(s.fromString)
66
+
67
+ if (request.startsWith("GET /")) {
68
+ respond("HTTP/1.1 200 OK\r\n\r\nHello from Effekt!")
66
69
  } else {
67
- do send("HTTP/1.1 400 Bad Request\r\n\r\n".fromUTF8)
70
+ respond("HTTP/1.1 400 Bad Request\r\n\r\n")
68
71
  }
69
72
  do end()
70
73
  }
@@ -62,7 +62,7 @@ def encodeJsonObject[R]{ body: => R / JsonObjectBuilder }: R / emit[String] = {
62
62
  do emit("{")
63
63
  def c(k: String) = {
64
64
  if (not(first)) { do emit(",") }
65
- do emit(escape(k)); do emit(":")
65
+ do emit(escape(k)); do emit(":")
66
66
  first = false
67
67
  }
68
68
  val r = encodeJson {
@@ -76,7 +76,7 @@ def encodeJsonObject[R]{ body: => R / JsonObjectBuilder }: R / emit[String] = {
76
76
  /// Main entry point for encoding json.
77
77
  /// Emits individual tokens of the resulting json.
78
78
  def encodeJson[R]{ body: => R / JsonBuilder }: R / emit[String] = {
79
- try body() with JsonBuilder {
79
+ try body() with JsonBuilder {
80
80
  def null() = { resume(do emit("null")) }
81
81
  def bool(v) = { resume(do emit( if(v){ "true" } else { "false" } )) }
82
82
  def number(n) = { resume(do emit(show(n))) }
@@ -112,7 +112,7 @@ def readDouble(): Double / Scan[Char] = {
112
112
  if (optionally { readIf('.') }) {
113
113
  var b = 0.1
114
114
  var r = pre.toDouble
115
- while (optionally[Int] { readDigit() } is Some(d)) {
115
+ while (returning::optionally[Int] { readDigit() } is Some(d)) {
116
116
  r = r + b * d.toDouble
117
117
  b = b * 0.1
118
118
  }
@@ -126,14 +126,14 @@ def readDouble(): Double / Scan[Char] = {
126
126
 
127
127
  def expectString(string: String): Unit / { Scan[Char], Exception[WrongFormat] } =
128
128
  for[Char] { string.each } { char =>
129
- expect[Unit]("Expected " ++ string) { readIf(char) }
129
+ expect("Expected " ++ string) { readIf(char) }
130
130
  }
131
131
 
132
132
  /// Read and unescape a string in ""
133
133
  def readQuotedString(): Unit / { Scan[Char], emit[Char], Exception[WrongFormat] } = {
134
134
  try {
135
135
  skipWhitespace()
136
- expect[Unit]("Expected \"") { readIf('"') }
136
+ expect("Expected \"") { readIf('"') }
137
137
  while(read[Char]() is c and c != '"') {
138
138
  c match {
139
139
  case '\\' => read[Char]() match {
@@ -191,7 +191,7 @@ def decodeJsonObject(): Unit / {Scan[Char], JsonObjectBuilder, Exception[WrongFo
191
191
  }
192
192
  do skip[Char]()
193
193
  }
194
- def decodeJsonList(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} = {
194
+ def decodeJsonList(): Unit / {Scan[Char], JsonBuilder, Exception[WrongFormat]} = {
195
195
  var first = true
196
196
  expectString("[")
197
197
  with boundary
@@ -278,7 +278,7 @@ def build[R](){ body: => R / JsonBuilder }: (R, JsonValue) = {
278
278
  }
279
279
  (x, r)
280
280
  }
281
- def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = collectList[JsonValue, R] {
281
+ def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = returning::collectList[JsonValue, R] {
282
282
  try body() with JsonBuilder {
283
283
  def number(n) = { do emit(Number(n)); resume(()) }
284
284
  def bool(b) = { do emit(Bool(b)); resume(()) }
@@ -296,7 +296,7 @@ def buildList[R](){ body: => R / JsonBuilder }: (R, List[JsonValue]) = collectLi
296
296
  }
297
297
  }
298
298
  }
299
- def buildDict[R](){ body: => R / JsonObjectBuilder }: (R, List[(String, JsonValue)]) = collectList[(String, JsonValue), R] {
299
+ def buildDict[R](){ body: => R / JsonObjectBuilder }: (R, List[(String, JsonValue)]) = returning::collectList[(String, JsonValue), R] {
300
300
  try body() with JsonObjectBuilder {
301
301
  def field(k) = resume { {v} =>
302
302
  val x = build{v}
@@ -315,10 +315,12 @@ namespace test {
315
315
 
316
316
  // Read quoted string
317
317
  feed("\"\ta\n\ra\"") {
318
- with scanner[Char, Unit]
318
+ with scanner[Char]
319
319
  println(collectString { readQuotedString() })
320
320
  }
321
321
 
322
+ println("")
323
+
322
324
  // Parse example
323
325
  feed("""{ "a": null, "b": [true,false,false,true], "f": 12.532 }"""){
324
326
  with scanner[Char]
@@ -336,8 +338,10 @@ namespace test {
336
338
  }
337
339
  handleJsonBuilder{d}{ decodeJson() }
338
340
  }
339
- try{
340
-
341
+
342
+ println("")
343
+
344
+ try {
341
345
  // Encode example
342
346
  encodeJson {
343
347
  do dict{
@@ -352,13 +356,15 @@ namespace test {
352
356
  }
353
357
  }
354
358
  }
355
-
359
+
360
+ println("")
361
+
356
362
  // format with intermediate value
357
- val j = feed("""{
358
- "a": null,
359
- "b": [true, false, false, true], "f": 12.532
363
+ val j = feed[JsonValue]("""{
364
+ "a": null,
365
+ "b": [true, false, false, true], "f": 12.532
360
366
  } """){
361
- with scanner[Char, JsonValue]
367
+ with returning::scanner[Char, JsonValue]
362
368
  build {
363
369
  decodeJson()
364
370
  }.second
@@ -366,18 +372,20 @@ namespace test {
366
372
  encodeJson{
367
373
  unbuild(j)
368
374
  }
369
-
375
+
376
+ println("")
377
+
370
378
  // format (minify) example
371
379
  encodeJson{
372
- feed("""{
373
- "a": null,
374
- "b": [true, false, false, true], "f": 12.532
380
+ feed("""{
381
+ "a": null,
382
+ "b": [true, false, false, true], "f": 12.532
375
383
  } """){
376
- with scanner[Char, Unit]
384
+ with scanner[Char]
377
385
  decodeJson()
378
386
  }
379
387
  }
380
-
388
+
381
389
  } with emit[String] { e =>
382
390
  resume(println(e))
383
391
  }
@@ -670,35 +670,113 @@ def partition[A](l: List[A]) { pred: A => Bool }: (List[A], List[A]) = {
670
670
  (lefts.reverse, rights.reverse)
671
671
  }
672
672
 
673
- /// Sort a list using a given comparison function.
673
+ /// Utilities for sorting, see 'sortBy' for more details.
674
+ namespace sort {
675
+ /// Splits the given list into monotonic segments (so a list of lists).
676
+ ///
677
+ /// Internally used in the mergesort 'sortBy' to prepare the to-be-merged partitions.
678
+ def sequences[A](list: List[A]) { compare: (A, A) => Bool }: List[List[A]] = list match {
679
+ case Cons(a, Cons(b, rest)) =>
680
+ if (compare(a, b)) {
681
+ ascending(b, rest) { diffRest => Cons(a, diffRest) } {compare}
682
+ } else {
683
+ descending(b, [a], rest) {compare}
684
+ }
685
+ case _ => [list]
686
+ }
687
+
688
+ /// When in an ascending sequence, try to add `current` to `run` (if possible)
689
+ def ascending[A](current: A, rest: List[A]) { runDiff: List[A] => List[A] } { compare: (A, A) => Bool }: List[List[A]] = rest match {
690
+ case Cons(next, tail) and compare(current, next) =>
691
+ ascending(next, tail) { diffRest => runDiff(Cons(current, diffRest)) } {compare}
692
+ case _ => Cons(runDiff([current]), sequences(rest) {compare})
693
+ }
694
+
695
+ /// When in an descending sequence, try to add `current` to `run` (if possible)
696
+ def descending[A](current: A, run: List[A], rest: List[A]) { compare: (A, A) => Bool }: List[List[A]] = rest match {
697
+ case Cons(next, tail) and not(compare(current, next)) =>
698
+ descending(next, Cons(current, run), tail) {compare}
699
+ case _ => Cons(Cons(current, run), sequences(rest) {compare})
700
+ }
701
+
702
+ def mergeAll[A](runs: List[List[A]]) { compare: (A, A) => Bool }: List[A] = runs match {
703
+ case Cons(single, Nil()) => single
704
+ case _ => {
705
+ // recursively merge in pairs until there's only a single list
706
+ val newRuns = mergePairs(runs) {compare}
707
+ mergeAll(newRuns) {compare}
708
+ }
709
+ }
710
+
711
+ def mergePairs[A](runs: List[List[A]]) { compare: (A, A) => Bool }: List[List[A]] = runs match {
712
+ case Cons(a, Cons(b, rest)) =>
713
+ Cons(merge(a, b) {compare}, mergePairs(rest) {compare})
714
+ case _ => runs
715
+ }
716
+
717
+ def merge[A](l1: List[A], l2: List[A]) { compare: (A, A) => Bool }: List[A] =
718
+ (l1, l2) match {
719
+ case (Nil(), _) => l2
720
+ case (_, Nil()) => l1
721
+ case (Cons(h1, t1), Cons(h2, t2)) =>
722
+ if (compare(h1, h2)) {
723
+ Cons(h1, merge(t1, l2) {compare})
724
+ } else {
725
+ Cons(h2, merge(l1, t2) {compare})
726
+ }
727
+ }
728
+ }
729
+
730
+ /// Sort a list given a comparison operator (like less-or-equal!)
731
+ /// The sorting algorithm is stable and should act reasonably well on partially sorted data.
732
+ ///
733
+ /// Examples:
734
+ /// ```
735
+ /// > [1, 3, -1, 5].sortBy { (a, b) => a <= b }
736
+ /// [-1, 1, 3, 5]
737
+ ///
738
+ /// > [1, 3, -1, 5].sortBy { (a, b) => a >= b }
739
+ /// [5, 3, 1, -1]
740
+ ///
741
+ /// > [(1, 0), (0, 1), (-1, 1), (0, 0)].sortBy { (a, b) => a.first + a.second <= b.first + b.second }
742
+ /// [(1, -1), (0, 0), (1, 0), (0, 1)]
743
+ ///
744
+ /// > Nil[Int]().sortBy { (a, b) => a <= b }
745
+ /// []
746
+ /// ```
674
747
  ///
675
748
  /// Note: this implementation is not stacksafe!
749
+ /// (works for ~5M random elements just fine, but OOMs on ~10M random elements)
676
750
  ///
677
- /// O(N log N)
678
- def sortBy[A](l: List[A]) { compare: (A, A) => Bool }: List[A] =
679
- l match {
680
- case Nil() => Nil()
681
- case Cons(pivot, rest) =>
682
- val (lt, gt) = rest.partition { el => compare(el, pivot) };
683
- val leftSorted = sortBy(lt) { (a, b) => compare(a, b) }
684
- val rightSorted = sortBy(gt) { (a, b) => compare(a, b) }
685
- leftSorted.append(Cons(pivot, rightSorted))
686
- }
751
+ /// O(N log N) worstcase
752
+ def sortBy[A](list: List[A]) { lessOrEqual: (A, A) => Bool }: List[A] = {
753
+ val monotonicRuns = sort::sequences(list) {lessOrEqual}
754
+ sort::mergeAll(monotonicRuns) {lessOrEqual}
755
+ }
756
+
757
+ /// Sort a list of integers in an ascending order.
758
+ /// See 'sortBy' for more details.
759
+ ///
760
+ /// O(N log N) worstcase
761
+ def sort(l: List[Int]): List[Int] = l.sortBy { (a, b) => a <= b }
762
+
763
+ /// Sort a list of doubles in an ascending order.
764
+ /// See 'sortBy' for more details.
765
+ ///
766
+ /// O(N log N) worstcase
767
+ def sort(l: List[Double]): List[Double] = l.sortBy { (a, b) => a <= b }
687
768
 
688
- def sort(l: List[Int]): List[Int] = l.sortBy { (a, b) => a < b }
689
- def sort(l: List[Double]): List[Double] = l.sortBy { (a, b) => a < b }
690
769
 
691
- /// Check if a list is sorted according to the given comparison function.
770
+ /// Check if a list is sorted according to the given comparison function (less-or-equal).
692
771
  ///
693
772
  /// O(N)
694
- def isSortedBy[A](list: List[A]) { compare: (A, A) => Bool }: Bool = {
773
+ def isSortedBy[A](list: List[A]) { lessOrEqual: (A, A) => Bool }: Bool = {
695
774
  def go(list: List[A]): Bool = {
696
775
  list match {
697
- case Nil() => true
698
- case Cons(x, Nil()) => true
699
776
  case Cons(x, Cons(y, rest)) =>
700
777
  val next = Cons(y, rest) // Future work: Replace this by an @-pattern!
701
- compare(x, y) && go(next)
778
+ lessOrEqual(x, y) && go(next)
779
+ case _ => true
702
780
  }
703
781
  }
704
782
  go(list)