Replies: 3 comments 4 replies
-
Ported from #393 (comment)
I don't think there is an equivalent in Scala! afaik, we were confined to expressing this kind of thing with monadic idioms, we'd either need a binding operator or some kind of callback (e.g., the afaiu, when taken to its full generality -- in Haskell's However, afaiu, the fact that we can move our reasoning into our effect systems opens up the opportunity to support the direct style proposed by @bugarela. I can explain this in more detail if that would be helpful. But the gist is basically that, if we are using the monadic idiom to, e.g., sequence partial computations in Scala, we can do stuff like: scala> val x : Option[Int] = for {
x <- Some(1);
y <- Some(x + 1)
} yield x + y
x: Option[Int] = Some(3)
scala> val x : Option[Int] = for {
x <- Some(1);
y <- None
} yield x + (y : Int)
x: Option[Int] = None And the scala> val x : Option[Int] =
Some(1).flatMap( x =>
Some(x + 1).flatMap( y =>
Option(x + y)))
x: Option[Int] = Some(3) In this context, Now, the important point of comparison here w/r/t the suggestion that we allow scala> val x : Option[Int] = for {
y <- Some(Some(1) + 1)
} yield Some(1) + y
<nope!> I.e., we cannot substitute However, with access to an effect system, we can reason about and handle effects in a "direct style". I think this is what @bugarela and I have been advocating for. As a point of comparison, this is what it can look like to leverage an effect system (in this case, OCaml 5's) to manage partial computations in a direct style: let%test_module "partial computation effects" = (module struct
open Partial
let (/) n = function
| 0. -> none ()
| d -> some (Float.div n d)
let head = function
| [] -> none ()
| x :: _ -> some x
let tail = function
| [] -> none ()
| _ :: tl -> some tl
let%test "direct style with successful operations" =
let progn () =
(head [4. ; 3. ; 2.]) / 2. :: tail [2. ; 3. ; 4.]
in
Optional.handler progn () = Some [2. ; 3. ; 4.]
let failing_progn () =
let z = 1. / 0. in
let x = head [z ; 2. ; 3.] in
let y = tail [1.] in
x :: y
let%test "direct style with a failing operation" =
Optional.handler failing_progn () = None Note that we can apply the partial operations If we were bound to using mondaic idioms, we'd have to write the for {
h <- head(List(4, 3, 2));
x <- y / 2 ;
tl <- tail(List(2, 3, 4));
} yield x :: tl I.e., we'd have to bind each intermediate optional value. I think this is roughly what we get if we end up requiring the |
Beta Was this translation helpful? Give feedback.
-
Trending on Hacker News and similar is a discussion around the Verse programming language, which is a functional logic programming language, like Mercury or Curry, that includes first-class data-nondeterminism. |
Beta Was this translation helpful? Give feedback.
-
I know that we still have some disagreements on |
Beta Was this translation helpful? Give feedback.
-
A special place for discussing this topic that we've been kicking around for a while.
There is some concern that if we expose non-deterministic constraints or oracular inputs -- or however we ought to think of this -- to spec authors that they will be confused or end up writing specs that don't behavior as they'd expect. (Actually, I realized I'm not 100% clear on the concern.)
A key discussion on this may be #377 (comment) In particular, see @bugarela's proposal for extending the effect system to account for nondet effects in a general way: #377 (comment)
This is proposed as an alternative to what @konnov has been advancing, which is a syntax that has special rules for nondet values, only allowing them to appear on the rhs of a binding prefixed with a keyword (viz.
nondet
) and only allowing these bindings in particular contexts (e.g., not on the top level, and only in operators that haveUpdate
).Beta Was this translation helpful? Give feedback.
All reactions