rescript-lang / syntax

ReScript's syntax as a standalone repo.
MIT License
256 stars 38 forks source link

Infix operator support #440

Closed IwanKaramazow closed 1 year ago

IwanKaramazow commented 3 years ago

Implement parsing of operator tokens for value bindings and value description without escaping.

snatvb commented 3 years ago

Could you explain? I think that I will be able to write own operator -- :+: for example. And use

let sum = (a, b) => a :+: b
digitake commented 3 years ago

We need this. It's essential for a language to be used as DSL. That's one of the major reasons.

chrischen commented 3 years ago

Use cases like >=> can be replaced with function calls to flatMap or bind, but I can't find a good looking workaround for doing stuff like...

Where <$> is a Option.map function and <*> is Option.apply. A function someFunc which takes pure int inputs can be converted to take monad types like option(int).

someFunc: (int, int) => int

becomes

someFunc: (option(int), option(int)) => option(int)

Infixes create easier to read code that looks almost like a regular function call someFunc(param2, param2)

someFunc <$> param1 <*> param2

But restricting infixes means they have to be rewritten like this.

apply(map(someFunc, param1), param2)
TheSpyder commented 3 years ago

Infixes create easier to read code that looks almost like a regular function call someFunc(param2, param2)

someFunc <$> param1 <*> param2

But restricting infixes means they have to be rewritten like this.

apply(map(someFunc, param1), param2)

The solution for the moment is pipe:

someFunc->map(param1)->apply(param2)

Whether it's better or worse is the entire debate around this feature. I personally agree the infix is nicer once you get used to it, I prefer both the symbolic nature and the lack of brackets required. But all three should compile to the same JS.

chrischen commented 3 years ago

I would also like to add that this is a standard thing supported in F#, Haskell, Reason., Ocaml, and many other functional languages, so it’s not just some esoteric domain specific use of infixes.

Since the -> syntax works almost the same was as an infix (and can be defined as an infix) I don’t think it’s that much of a mental leap for newcomers to learn and parse infixes. Plus they’d be learning well established paradigms.

Thanks! Chris Creative Consultant


From: Andrew Herron @.> Sent: Thursday, October 7, 2021 9:39:12 PM To: rescript-lang/syntax @.> Cc: chrischen @.>; Comment @.> Subject: Re: [rescript-lang/syntax] Infix operator support (#440)

Infixes create easier to read code that looks almost like a regular function call someFunc(param2, param2)

someFunc <$> param1 <*> param2

But restricting infixes means they have to be rewritten like this.

apply(map(someFunc, param1), param2)

The solution for the moment is pipe:

someFunc->map(param1)->apply(param2)

Whether it's better or worse is the entire debate around this feature. I personally agree the infix is nicer once you get used to it, I prefer both the symbolic nature and the lack of brackets required. But all three should compile to the same JS.

— You are receiving this because you commented. Reply to this email directly, view it on GitHubhttps://github.com/rescript-lang/syntax/issues/440#issuecomment-938292543, or unsubscribehttps://github.com/notifications/unsubscribe-auth/AAA2YPWM7VB2H7DF5DD6DSLUFZKVBANCNFSM46Y3J26A. Triage notifications on the go with GitHub Mobile for iOShttps://apps.apple.com/app/apple-store/id1477376905?ct=notification-email&mt=8&pt=524675 or Androidhttps://play.google.com/store/apps/details?id=com.github.android&referrer=utm_campaign%3Dnotification-email%26utm_medium%3Demail%26utm_source%3Dgithub.

bencef commented 3 years ago

I tried to trick it by writing the operator in an OCaml file :)

Distrib.ml

type 'a t = ('a * float) array

let pure v = [| (v, 1.0) |]

let uniform vals =
  let len = vals |> Array.length |> float_of_int in
  vals |> Array.map (fun v -> (v, 1.0 /. len))

let map f v = v |> Array.map (fun (v, c) -> (f v, c))

let bind ma mf =
  ma
  |> Array.map (fun (v, c) ->
         mf v
         |> Array.map (fun (v', c') -> (v', c *. c')))
  |> Array.to_list
  |> Array.concat

module Syntax =
struct
  (* let ( let+ ) = map *)
  (* let ( let* ) = bind *)
  let ( >>= ) = bind
end

And use it from from ReScript

let foo = () => {
  open Distrib.Syntax
  let d6 = [ 1, 2, 3, 4, 5, 6 ]->Distrib.uniform
  d6 >>= (num => Distrib.pure (num < 4))
}

But I get a Syntax error :)

Sadly pipelines are not a cure for monadic syntax. See:

let chance_of_seven = () => {
  open Distrib
  let d6 = [1, 2, 3, 4, 5, 6]->uniform
  d6->bind(firstRoll =>
    d6->bind(secondRoll => pure(firstRoll + secondRoll === 7))
  )
}
bencef commented 3 years ago

Note: Infix >>= wouldn't help here either. I'd love to have let* syntax. But as far as I can tell even the OCaml compiler coming with rescript doesn't have it

TheSpyder commented 3 years ago

yes, because ReScript doesn't have infix they can't be defined or used. The translation to use those functions right now is \">>="(d6, num => Distrib.pure(num < 4)).

Note: Infix >>= wouldn't help here either. I'd love to have let* syntax. But as far as I can tell even the OCaml compiler coming with rescript doesn't have it

Indeed. It's based on 4.06, let* was added in 4.08.

stale[bot] commented 1 year ago

The rescript-lang/syntax repo is obsolete and will be archived soon. If this issue is still relevant, please reopen in the compiler repo (https://github.com/rescript-lang/rescript-compiler) or comment here to ask for it to be moved. Thank you for your contributions.