I am doing some exercise from the book Grokking Functional Programming, the origin code examples are written in Scala, and I want to rewrite it with Raku.
In Scala's cats effect library, there is a type call IO, which is a data type for encoding side effects as pure values, capable of expressing both synchronous and asynchronous computations.IO[A]
is a value that represents a potentially side-effectful
IO action (or another unsafe operation) that, if successful,
produces a value of type A.
The following code print "hey!" twice:
import cats.effect.IO
import cats.effect.unsafe.implicits.global
object TestApp extends App {
val program: IO[Unit] = for {
_ <- IO.println("hey!")
_ <- IO.println("hey!")
} yield ()
program.unsafeRunSync()
}
The code can be run in Scala playground.
The corresponding data type in Raku I can think of is Promise, or some other Lazy data type I dont't know yet.
So, does Raku has a data type for encoding side effects as pure values?
Using Raku's type-system it's fairly simple to implement monadic types like IO. Something like:
role IO[$t] does Callable {
has $.type = $t;
has &.cb;
method map(&f) {
return IO[$!type].new(cb => &f(&!cb));
}
method bind(&f) {
return &f(&!cb);
}
submethod CALL-ME(*@t) {
&!cb();
}
}
sub sayIO($s --> IO[Nil]) {
# Use Nil in place of unit.
return IO[Nil].new(cb => sub { say $s });
}
sub execAllSync(@ios --> Any) {
$_() for @ios;
}
sub execAllAsync(@ios --> Promise) {
my Promise @promises;
for @ios -> $a {
push @promises, start { $a(); }
}
Promise.allof(|@promises);
}
execAllSync [sayIO("foo"), sayIO("bar"), sayIO("baz")];
await execAllAsync([sayIO("foo"), sayIO("bar"), sayIO("baz")]);
There may also be some type-fu you can do using given
to create a more monadic interface, similar to what is in Monad-Result. But I'm not 100% sure that it is super useful. The side-effect is just the execution of the code, so all you really need is a wrapper around a function.