@effekt-lang/effekt 0.60.0 → 0.62.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
@@ -60,6 +60,8 @@
60
60
  out))
61
61
 
62
62
 
63
+ (define (print_impl str)
64
+ (display str))
63
65
 
64
66
  (define (println_impl str)
65
67
  (display str)
@@ -140,4 +142,4 @@
140
142
  (raise
141
143
  (condition
142
144
  (make-error)
143
- (make-message-condition ("should not be reachable!")))))
145
+ (make-message-condition ("should not be reachable!")))))
@@ -624,7 +624,9 @@ def sorted[A](arr: Array[A]) { ord: (A, A) => Ordering }: Array[A] = {
624
624
  // Show Instances
625
625
  // --------------
626
626
 
627
- def show[A](arr: Array[A]) { showA: A => String }: String = {
627
+ // TODO: These can eventually be replaced if we ever figure out monomorphization on extern types
628
+
629
+ def showBuiltin[A](arr: Array[A]) { showA: A => String }: String = {
628
630
  var output = "Array("
629
631
  val lastIndex = arr.size - 1
630
632
 
@@ -636,16 +638,16 @@ def show[A](arr: Array[A]) { showA: A => String }: String = {
636
638
 
637
639
  output
638
640
  }
639
- def show(l: Array[Int]): String = show(l) { e => show(e) }
640
- def show(l: Array[Double]): String = show(l) { e => show(e) }
641
- def show(l: Array[Bool]): String = show(l) { e => show(e) }
642
- def show(l: Array[String]): String = show(l) { e => e }
643
641
 
644
- def println(l: Array[Int]): Unit = println(show(l))
645
- def println(l: Array[Double]): Unit = println(show(l))
646
- def println(l: Array[Bool]): Unit = println(show(l))
647
- def println(l: Array[String]): Unit = println(show(l))
642
+ def showBuiltin(l: Array[Int]): String = showBuiltin(l) { e => show(e) }
643
+ def showBuiltin(l: Array[Double]): String = showBuiltin(l) { e => show(e) }
644
+ def showBuiltin(l: Array[Bool]): String = showBuiltin(l) { e => show(e) }
645
+ def showBuiltin(l: Array[String]): String = showBuiltin(l) { e => e }
648
646
 
647
+ def println(l: Array[Int]): Unit = println(showBuiltin(l))
648
+ def println(l: Array[Double]): Unit = println(showBuiltin(l))
649
+ def println(l: Array[Bool]): Unit = println(showBuiltin(l))
650
+ def println(l: Array[String]): Unit = println(showBuiltin(l))
649
651
 
650
652
  // Streaming
651
653
  // ---------
@@ -75,18 +75,10 @@ def popBack[R](dq: Dequeue[R]): Option[(R, Dequeue[R])] = dq match {
75
75
  Some((x, Dequeue(f, fs, rest, rs - 1).check))
76
76
  }
77
77
 
78
+ // TODO: Replace when monomorphization is merged
78
79
 
79
- def show[A](d: Dequeue[A]) { showA: A => String }: String = {
80
- val Dequeue(front, frontSize, rear, rearSize) = d;
81
- "Dequeue(" ++ show(front){showA} ++ ", " ++ show(frontSize) ++ ", " ++
82
- show(rear){showA} ++ ", " ++ show(rearSize) ++ ")"
83
- }
84
- def show(l: Dequeue[Int]): String = show(l) { e => show(e) }
85
- def show(l: Dequeue[Double]): String = show(l) { e => show(e) }
86
- def show(l: Dequeue[Bool]): String = show(l) { e => show(e) }
87
- def show(l: Dequeue[String]): String = show(l) { e => e }
88
80
 
89
81
  def println(l: Dequeue[Int]): Unit = println(show(l))
90
82
  def println(l: Dequeue[Double]): Unit = println(show(l))
91
83
  def println(l: Dequeue[Bool]): Unit = println(show(l))
92
- def println(l: Dequeue[String]): Unit = println(show(l))
84
+ def println(l: Dequeue[String]): Unit = println(show(l))
@@ -38,6 +38,16 @@ namespace llvm {
38
38
  // Showing and debugging
39
39
  // =====================
40
40
 
41
+ extern def print(value: String): Unit =
42
+ jsWeb "$effekt.println(${value})"
43
+ jsNode "$effekt.print(${value})"
44
+ chez "(print_impl ${value})"
45
+ llvm """
46
+ call void @c_io_print(%Pos ${value})
47
+ ret %Pos zeroinitializer ; Unit
48
+ """
49
+ vm "effekt::print(String)"
50
+
41
51
  extern def println(value: String): Unit =
42
52
  js "$effekt.println(${value})"
43
53
  chez "(println_impl ${value})"
@@ -74,7 +84,7 @@ def println(value: Double): Unit = println(value.show)
74
84
  def println(value: Bool): Unit = println(value.show)
75
85
  def println(value: Byte): Unit = println(value.show)
76
86
 
77
- extern def show(value: Int) at {}: String =
87
+ extern def showBuiltin(value: Int) at {}: String =
78
88
  js "'' + ${value}"
79
89
  chez "(show-number ${value})"
80
90
  llvm """
@@ -83,9 +93,7 @@ extern def show(value: Int) at {}: String =
83
93
  """
84
94
  vm "effekt::show(Int)"
85
95
 
86
- def show(value: Unit): String = "()"
87
-
88
- extern def show(value: Double) at {}: String =
96
+ extern def showBuiltin(value: Double) at {}: String =
89
97
  js "'' + ${value}"
90
98
  chez "(show-number ${value})"
91
99
  llvm """
@@ -94,13 +102,7 @@ extern def show(value: Double) at {}: String =
94
102
  """
95
103
  vm "effekt::show(Double)"
96
104
 
97
- def show(value: String): String =
98
- value
99
-
100
- def show(value: Bool): String =
101
- if (value) "true" else "false"
102
-
103
- extern def show(value: Char) at {}: String =
105
+ extern def showBuiltin(value: Char) at {}: String =
104
106
  js "String.fromCodePoint(${value})"
105
107
  chez "(string (integer->char ${value}))"
106
108
  llvm """
@@ -109,22 +111,17 @@ extern def show(value: Char) at {}: String =
109
111
  """
110
112
  vm "effekt::show(Char)"
111
113
 
112
- extern def show(value: Byte) at {}: String =
113
- js "'' + ${value}"
114
- chez "(string ${value})"
114
+ extern def showBuiltin(value: Byte) at {}: String =
115
+ js "'0x' + ${value}.toString(16).toUpperCase().padStart(2, '0')"
116
+ chez "(format #f \"0x~2,'0X\" ${value})"
115
117
  llvm """
116
- %z = call %Pos @c_bytearray_show_Byte(i8 ${value})
118
+ %z = call %Pos @c_bytearray_show_Byte(%Byte ${value})
117
119
  ret %Pos %z
118
120
  """
119
121
  vm "effekt::show(Byte)"
120
122
 
121
- extern def genericShow[R](value: R) at {}: String =
122
- js "$effekt.show(${value})"
123
- chez "(show_impl ${value})"
124
-
125
123
  extern def inspect[R](value: R) at io: Unit =
126
- js { println(genericShow(value)) }
127
- chez { println(genericShow(value)) }
124
+ default { println(show(value)) }
128
125
  vm "effekt::inspect(Any)"
129
126
 
130
127
  /// A fatal runtime error that cannot be caught
@@ -137,6 +134,10 @@ extern def panic(msg: String) at io: Nothing =
137
134
  ret %Pos zeroinitializer ; Unit
138
135
  """
139
136
 
137
+ extern def show[R](value: R) at {}: String =
138
+ js "$effekt.show(${value})"
139
+ chez "(show_impl ${value})"
140
+ llvm "ret %Pos poison"
140
141
 
141
142
  // Strings
142
143
  // =======
@@ -223,12 +224,6 @@ def genericCompare[R](x: R, y: R): Ordering = {
223
224
  }
224
225
  }
225
226
 
226
- def show(o: Ordering): String = o match {
227
- case Less() => "Less()"
228
- case Equal() => "Equal()"
229
- case Greater() => "Greater()"
230
- }
231
-
232
227
  def println(o: Ordering): Unit = println(o.show)
233
228
 
234
229
 
@@ -795,9 +790,9 @@ def locally[R] { p: => R } : R = p()
795
790
  //
796
791
  // "foo ${42} bar ${43}"
797
792
  //
798
- // to the following stream of `literal`s and `splice[Int]`s:
793
+ // to the following stream of `write`s and `splice[Int]`s:
799
794
  //
800
- // do literal("foo "); do splice(42); do literal(" bar "); do splice(43)
795
+ // do write("foo "); do splice(42); do write(" bar "); do splice(43)
801
796
  //
802
797
  // The stream is wrapped into a handler function, which defaults to `stringbuffer::s`
803
798
  //
@@ -805,7 +800,8 @@ def locally[R] { p: => R } : R = p()
805
800
  //
806
801
  // but can be specified by prefixing the string interpolation `custom"..."`.
807
802
 
808
- effect literal(s: String): Unit
803
+ effect write(s: String): Unit
804
+
809
805
  effect splice[A](x: A): Unit
810
806
 
811
807
 
@@ -74,6 +74,16 @@ def tail[A](l: List[A]): List[A] / Exception[MissingValue] = l match {
74
74
  case Cons(a, rest) => rest
75
75
  }
76
76
 
77
+ /// Return all elements of a given list except the last element.
78
+ /// Throws a `MissingValue` exception if it's empty.
79
+ ///
80
+ /// O(n)
81
+ def init[A](l: List[A]): List[A] / Exception[MissingValue] = l match {
82
+ case Nil() => do raise(MissingValue(), "Trying to get the beginning of an empty list")
83
+ case Cons(a, Nil()) => Nil()
84
+ case Cons(a, as) => Cons(a, as.init)
85
+ }
86
+
77
87
  /// Return the first element of a given list.
78
88
  /// Returns `None()` if it's empty.
79
89
  ///
@@ -914,24 +924,15 @@ def isSortedBy[A](list: List[A]) { lessOrEqual: (A, A) => Bool }: Bool = {
914
924
  // Show Instances
915
925
  // --------------
916
926
 
917
- def show[A](l: List[A]) { showA: A => String }: String = {
918
- def go(l: List[A]): String = l match {
919
- case Nil() => "Nil()"
920
- case Cons(x, xs) => "Cons(" ++ showA(x) ++ ", " ++ go(xs) ++ ")"
921
- }
922
- go(l)
923
- }
924
- def show(l: List[Int]): String = show(l) { e => show(e) }
925
- def show(l: List[Double]): String = show(l) { e => show(e) }
926
- def show(l: List[Bool]): String = show(l) { e => show(e) }
927
- def show(l: List[String]): String = show(l) { e => e }
927
+ // TODO: Get rid of these after monomorphization is merged
928
+
928
929
 
929
930
  def println(l: List[Int]): Unit = println(show(l))
930
931
  def println(l: List[Double]): Unit = println(show(l))
932
+ def println(l: List[Char]): Unit = println(show(l))
931
933
  def println(l: List[Bool]): Unit = println(show(l))
932
934
  def println(l: List[String]): Unit = println(show(l))
933
935
 
934
-
935
936
  // Streaming
936
937
  // ---------
937
938
 
@@ -969,7 +969,7 @@ namespace internal {
969
969
  t match {
970
970
  case Tip() => ""
971
971
  case Bin(_, k, v, l, r) =>
972
- val pair = k.genericShow ++ " → " ++ v.genericShow
972
+ val pair = k.show ++ " → " ++ v.show
973
973
  val currentLine = prefix ++ (if (isTail) "└── " else "├── ") ++ pair ++ "\n"
974
974
 
975
975
  val newPrefix = prefix ++ (if (isTail) " " else "│ ")
@@ -59,14 +59,7 @@ def undefinedToOption[A](value: A): Option[A] =
59
59
  // Show Instances
60
60
  // --------------
61
61
 
62
- def show[A](o: Option[A]) { showA: A => String }: String = o match {
63
- case None() => "None()"
64
- case Some(v) => "Some(" ++ showA(v) ++ ")"
65
- }
66
- def show(o: Option[Int]): String = show(o) { e => show(e) }
67
- def show(o: Option[Double]): String = show(o) { e => show(e) }
68
- def show(o: Option[Bool]): String = show(o) { e => show(e) }
69
62
 
70
63
  def println(o: Option[Int]): Unit = println(show(o))
71
64
  def println(o: Option[Double]): Unit = println(show(o))
72
- def println(o: Option[Bool]): Unit = println(show(o))
65
+ def println(o: Option[Bool]): Unit = println(show(o))
@@ -46,3 +46,19 @@ def suchThat[A](value: A){ predicate: A => Bool }: Option[A] =
46
46
 
47
47
  def getOrFail[A](option: Option[A]): A / fail =
48
48
  option.getOrElse { do fail() }
49
+
50
+ namespace returning {
51
+
52
+ def exhaustively[R](init: R) { program: () => R / fail }: R =
53
+ var res = init
54
+ try {
55
+ def go(): R = {
56
+ res = program()
57
+ go()
58
+ }
59
+ go()
60
+ } with fail {
61
+ res
62
+ }
63
+
64
+ }
@@ -51,7 +51,7 @@ def minstd(seed: Int): Unit / emit[Byte] = {
51
51
  ///
52
52
  /// Implementation is similar to `stream::source`, specialized for bytes and the `random` effect.
53
53
  def minstd(seed: Int) { randomnessReader: () => Unit / random }: Unit = {
54
- var next = box { 255.toByte } // sentinel value
54
+ var next = box { 0xFF } // sentinel value
55
55
  next = box {
56
56
  try {
57
57
  minstd(seed)
@@ -192,6 +192,10 @@ def countIf[A] { shouldCount: A => Bool } { stream: () => Unit / emit[A] }: Int
192
192
  count
193
193
  }
194
194
 
195
+ /// Count the number of emitted values.
196
+ def count[A] { stream: () => Unit / emit[A] }: Int =
197
+ countIf[A] { _ => true } {stream}
198
+
195
199
  def source[A] { stream: () => Unit / emit[A] } { reader: () => Unit / read[A] }: Unit =
196
200
  returning::source[A, Unit]{stream}{reader}
197
201
 
@@ -3,60 +3,58 @@ module stringbuffer
3
3
  import effekt
4
4
  import bytearray
5
5
 
6
- interface StringBuffer {
7
- def write(str: String): Unit
8
- def flush(): String
9
- }
6
+ effect flush(): String
7
+
8
+ def stringBuffer { program: () => Unit / write }: String =
9
+ flushableStringBuffer { program(); do flush() }
10
10
 
11
- def stringBuffer[A] { prog: => A / StringBuffer }: A = {
11
+ def flushableStringBuffer[A] { program: () => A / { write, flush } }: A = {
12
12
  val initialCapacity = 128
13
13
  var buffer = bytearray::allocate(initialCapacity)
14
14
  // next free index to write to
15
- var pos = 0
15
+ var position = 0
16
16
 
17
- def ensureCapacity(sizeToAdd: Int): Unit = {
18
- val cap = buffer.size - pos
19
- if (sizeToAdd <= cap) ()
17
+ def ensureCapacity(required: Int): Unit = {
18
+ val capacity = buffer.size - position
19
+ if (required <= capacity) ()
20
20
  else {
21
21
  // Double the capacity while ensuring the required capacity
22
- val newSize = max(buffer.size * 2, buffer.size + sizeToAdd)
22
+ val newSize = max(buffer.size * 2, buffer.size + required)
23
23
  buffer = buffer.resize(newSize)
24
24
  }
25
25
  }
26
26
 
27
- try { prog() }
28
- with StringBuffer {
29
- def write(str) = {
30
- val bytes = fromString(str)
31
- ensureCapacity(bytes.size)
32
- bytes.foreach { b =>
33
- buffer.set(pos, b)
34
- pos = pos + 1
35
- }
36
- resume(())
37
- }
38
- def flush() = {
39
- // resize (& copy) buffer to strip trailing zeros that otherwise would be converted into 0x00 characters
40
- val str = bytearray::resize(buffer, pos).toString()
41
- // NOTE: Keep the `buffer` as-is (no wipe, no realloc),
42
- // just reset the `pos` in case we want to use it again.
43
- pos = 0
44
- resume(str)
27
+ try {
28
+ program()
29
+ } with write { string =>
30
+ val bytes = fromString(string)
31
+ ensureCapacity(bytes.size)
32
+ bytes.foreach { b =>
33
+ buffer.set(position, b)
34
+ position = position + 1
45
35
  }
36
+ resume(())
37
+ } with flush {
38
+ val string = buffer.resize(position).toString()
39
+ position = 0
40
+ resume(string)
46
41
  }
47
42
  }
48
43
 
44
+ def splicing[T] { writer: T => Unit } { program: () => Unit / splice[T] }: Unit =
45
+ try { program() } with splice[T] { t => resume(writer(t)) }
46
+
47
+
49
48
  /// Handler for string interpolation using a string buffer
50
- def s { prog: () => Unit / { literal, splice[String] } }: String =
49
+ def s { program: () => Unit / { write, splice[String] } }: String =
51
50
  stringBuffer {
52
- try { prog(); do flush() }
51
+ try { program() }
53
52
  with splice[String] { x => resume(do write(x)) }
54
- with literal { s => resume(do write(s)) }
55
53
  }
56
54
 
57
55
  namespace examples {
58
56
  def main() = {
59
- with stringBuffer
57
+ with flushableStringBuffer
60
58
 
61
59
  do write("hello")
62
60
  do write(", world")
@@ -46,7 +46,7 @@ def assertEqual[A](obtained: A, expected: A, msg: String): Unit / Assertion =
46
46
  assertEqual(obtained, expected, msg) { (x, y) => x.equals(y) }
47
47
 
48
48
  def assertEqual[A](obtained: A, expected: A): Unit / { Assertion, Formatted } =
49
- assertEqual(obtained, expected) { (x, y) => x.equals(y) } { x => x.genericShow }
49
+ assertEqual(obtained, expected) { (x, y) => x.equals(y) } { x => x.show }
50
50
 
51
51
  def assertEqual[A](obtained: A, expected: A, msg: String) { equals: (A, A) => Bool }: Unit / Assertion =
52
52
  do assert(equals(obtained, expected), msg)
@@ -56,6 +56,17 @@ def assertEqual[A](obtained: A, expected: A) { equals: (A, A) => Bool } { show:
56
56
  // NOTE: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
57
57
  // Here's an accidental capture! Can we prevent this somehow nicely?
58
58
 
59
+ def assertNotThrown[E](ex: on[E]) { body: => Unit / Exception[E] }: Unit / Assertion =
60
+ ex.default { do assert(false, "Unexpected Exception") } {body}
61
+
62
+ def assertNoThrow[E] { body: => Unit / Exception[E] }: Unit / Assertion =
63
+ on[E].default{ do assert(false, "Unexpected Exception") } {body}
64
+
65
+ def assertThrown[E](ex: on[E]) { body: => Unit / Exception[E] }: Unit / Assertion =
66
+ do assert(ex.default { true } { body(); false }, "Expected Exception, but exited normally")
67
+
68
+ def assertThrows[E] { body: => Unit / Exception[E] }: Unit / Assertion =
69
+ do assert(on[E].default { true } { body(); false }, "Expected Exception, but exited normally")
59
70
 
60
71
  interface Test {
61
72
  def success(name: String, duration: Int): Unit
@@ -54,6 +54,10 @@ $effekt.println = function println$impl(str) {
54
54
  console.log(str); return $effekt.unit;
55
55
  }
56
56
 
57
+ $effekt.print = function println$impl(str) {
58
+ process.stdout.write(str); return $effekt.unit;
59
+ }
60
+
57
61
  $effekt.unit = { __unit: true }
58
62
 
59
63
  $effekt.unreachable = function() { throw "should not be reachable!" }
@@ -2,6 +2,8 @@
2
2
  #define EFFEKT_BYTEARRAY_C
3
3
 
4
4
  #include <string.h> // For memcopy
5
+ #include <stdint.h>
6
+ #include <inttypes.h>
5
7
 
6
8
  /** We represent bytearrays like positive types.
7
9
  *
@@ -115,8 +117,8 @@ struct Pos c_bytearray_show_Char(const uint64_t n) {
115
117
 
116
118
  // TODO do this in Effekt
117
119
  struct Pos c_bytearray_show_Byte(const Byte n) {
118
- char str[4]; // Byte values range from 0 to 255, 3 characters + null terminator
119
- snprintf(str, sizeof(str), "%" PRIu8, n);
120
+ char str[5]; // |"0x" ++ <2 hex digits> ++ '\0'| = 5
121
+ snprintf(str, sizeof str, "0x%02" PRIX8, (uint8_t)n);
120
122
  return c_bytearray_from_nullterminated_string(str);
121
123
  }
122
124
 
@@ -3,6 +3,7 @@
3
3
  declare i64 @c_get_argc()
4
4
  declare %Pos @c_get_arg(i64)
5
5
 
6
+ declare void @c_io_print(%Pos)
6
7
  declare void @c_io_println(%Pos)
7
8
  declare %Pos @c_io_readln()
8
9
  declare %Double @c_io_random()
@@ -7,11 +7,15 @@
7
7
  // Println and Readln and Random
8
8
  // -----------------------------
9
9
 
10
- void c_io_println(String text) {
10
+ void c_io_print(String text) {
11
11
  for (uint64_t j = 0; j < text.tag; ++j) {
12
12
  putchar(c_bytearray_data(text)[j]);
13
13
  };
14
14
  erasePositive(text);
15
+ }
16
+
17
+ void c_io_println(String text) {
18
+ c_io_print(text);
15
19
  putchar('\n');
16
20
  }
17
21