curimit / SugarCpp

SugarCpp is a language which can compile to C++11.
135 stars 13 forks source link

Overload novel operators #42

Open ozra opened 9 years ago

ozra commented 9 years ago

A cool idea would be to be able to overload "invented" operators, perhaps anything in utf-8 that doesn't clash with the basic operators.

Let's say:

Test (§) (a: const Test&, b: const Test&)
   // do stuff

foo : Test
bar : Test
a := foo § bar

or even wilder, DSL like capabilities, pattern matching, (inspired by Agda):

Test (_ § _) (a: const Test&, b: const Test&)
   // do stuff
Test (½ _) (a: const Test&)
   // do stuff
Test (_ crashed-into _ at _ kmph) (a: const Test&, b: const Test&, c: int)
   // do stuff

foo : Test
bar : Test
a := foo § bar
b := ½ a
c := a crashed-into b at 130 kmph

They would ofcourse have to be name mangled when transforming into C++.. And, as always, it would be the developers responsibility to not go crazy with the feature ;) Precedence could be given too, either through a number or preferably comparatively against an existing operator.

curimit commented 9 years ago

I have some alternative choices right now. Maybe you can try infix function, it is designed for this purpose, you can define any or use any existing functions as operators.

Test §(a: const Test&, b: const Test&)
   // do stuff

foo, bar, eee : Test
a := foo `§` bar `§` eee

If you want to use some function as a suffix operator, I introduce the extension method(Already implemented, but haven't update the document yet). It looks like you can have the ability to add member functions to any types, it is only a syntax sugar. Think of it like this: we just explicitly passed the this pointer to that function.

Test ½ (a: const Test&)
   // do stuff

string ToString(a: const Test&)

b := a @½() @ToString()

The reason why I suggest to use suffix chain rather than prefix operators is that suffix chain is much more natural: Imagine you are writing b := [3,2,3], then you want to sort it, you can just add @order-by((x)->x), then you want to filter the odd number, you can just add @filter((x) -> x%2 == 1), after that you can use @sum() to calculate the sum.

b := [3,2,3] @order-by((x) -> x) @filter((x) -> x%2 == 1) @sum()

DSL like capabilities is an interesting topic.

ozra commented 9 years ago

I do indeed use the infix notation when coding in LiveScript, it's simple and nice.

I like the idea of the suffix notation. It could of course be changed to use prior art notation of Haskell and LiveScript - [edit] I saw this is mentioned in issue #15 - "piping", that is:

b := [3,2,3] |> order-by((x) -> x) |> filter((x) -> x%2 == 1) |> sum()

In LS, I assume Haskell too, but haven't checked, you can also explicitly state where the param is passed with the underscore notation:

foo := (bar + 2) |> i-take-three-args(a, _, c)

(Non important:) What remains is ofcourse (SugarCpp side) name mangling of utf8 characters in order to be able to use them in C++. say §(a : int) -> int => int U_00A7(int a);. However, it does cause highly unreadable C++ code, if that is of importance to one, secondly, I don't think this (non-ascii letters) is of such importance that it's worth your time atm...

dobkeratops commented 9 years ago

I like the |> idea (it's in f# aswell ?) which seems like it can retrofit in C syntax fine;

also .method(..) calls are kind of like faking infix notation aswell and this is why D-style UFCS is so nice IMO. [3,2,3].order_by((x)->x).filter((x)->x%2==1).sum() . with UFCS they don't have to be methods of the class, you can write any free functions and call them that way. f(x,y) === x.f(y). they are a very simple way of doing 'extention methods' basically.

( as an aside I think rust's syntax generally works nicely here.. [3,2,3].order_by(|x| x).filter(|x| x%2==1).sum() .. if you can get used to reading the |..| as a bracket, it might need some syntax highlighting to clarify it.. )

I would be curious to add general operator overloading to my experiment too.

I introduce the extension method(Already implemented, but haven't update the document yet).

Rust lets you add extention methods with impl, but it's not as open as UFCS. they need you to add a 'trait' for them aswell, and it has restrictions ('only extend types in your library or with traits in your library') .. I much prefer it being completely open, people can already confer using the internet to get consistent names. anyway as I'd like to move toward compiling some of rust aswell if possible, I've added this impl { ..extentionmethods ..} syntax form aswell just without Rusts restrictions.. it's just like another way to add more free functions.

Maybe its' a bit messy having both (you end up with funny rules if theres' x::f(y) ); but I'm just being pragmatic adding features compatible with the mainstream starting point. i could just make a warning if 'free functions' have the same names as methods

ozra commented 9 years ago

also .method(..) calls are kind of like faking infix notation aswell and this is why D-style UFCS is so nice IMO. [3,2,3].order_by((x)->x).filter((x)->x%2==1).sum() .

I completely agree on this one. It would however require a more 'aware' compiler than just a "sugar-decoater". SugarCpp doesn't "know" if it's a method or a free function - or am I wrong @curimit ?

As for the UFCS, I might have mentioned this in another post - dunno - but - is it possible to use methods 'as functions' also (given that the argument is a matching class)? I mean, that they're completely interchangeable? I would love that. Also the global open-ness, that you can extend anything (ie monkey patching which I utilize a lot in LiveScript/JavaScript)

dobkeratops commented 9 years ago

is it possible to use methods 'as functions' also (given that the argument is a matching class)?

I know there is a proposal for C++ to do this aswell. I think its' a good idea.

I haven't implemented this yet, but it might work already.

I internally implement UFCS and method calls by putting all the parameters in one list and handing that to a matching/scoring algorithm; it tells it how many 'preceding arguments' there were , and gives 'bonus points' if the callers & callee have the same 'preceding argument ' count.

there is one difference: I do have the methods 'in the namespace of the struct' so it might not find them yet. I don't have anything like C++'s "ADL" (scanning namespaces depending on the arguments). One of many areas where i've had to cut corners to get anywhere ... I've seen estimates that a full C++ compiler is 10 man-years? this is about 2months work so far :)

dobkeratops commented 9 years ago

I completely agree on this one. It would however require a more 'aware' compiler than just a "sugar-decoater". SugarCpp doesn't "know" if it's a method or a free function - or am I wrong @curimit ?

perhaps SugarCpp still has the option of actually collecting extention methods into classes, so its' still much easier to write helper methods than in C++. That would explain the choice for extention methods instead of UFCS there. I've also added the 'impl{..} blocks from rust, so basically I've got 2 ways of doing the same thing ... maybe messy but it wasn't hard to add.