Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Custom operators in livescript #674

Closed
askucher opened this issue Feb 20, 2015 · 13 comments
Closed

Custom operators in livescript #674

askucher opened this issue Feb 20, 2015 · 13 comments

Comments

@askucher
Copy link

Concept

#Examples

(+<)  :: (first, second) -> first.push second
(-<)  :: (first, second) -> first.splice(first.index-of(second), 1)
(++)  :: (first, second) -> first.concat(second)  #already exists
(<$>) :: (first, second) -> /* monad bullshit inside */

Implementation

Notice each operator has own name
(+) :: (first, second) -> first + second 

#compiles into 
#var $$plusOperator = function (first, second) { return first + second } 

1 + 2 #=> $$plusOperator(1,2)

#----------------------------------------------------------------------------------------

(-) :: (first, second) -> first - second 

#compiles into 
#var $$minusOperator = function (first, second) { return first - second } 

1 - 2 #=> $$minusOperator(1,2)



#----------------------------------------------------------------------------------------

(-<) :: (first, second) -> first.splice(first.index-of(second), 1)

#compiles into 
#var $$minusLessOperator = function (first, second) { return first.splice(first.indexOf(second), 1) } 

[1,2,3,4,5] -< 1 #=> $$minusLessOperator(1,2)

#result [2,3,4,5]

Benefits

General:

  1. Livescript could became community driven language and next step to DSL. Community will become close to each other. Developers will provide own libraries. The best common one will become popular.
  2. Developers will be able to provide domain specific libraries for Math domain, Financial domain, etc

Details:

  1. All existent operators could predefined as custom operators which were used in current not bare scope
  2. Default parameters could be redefined. For instance (+) operator could be extended. by type checking of input arguments.
(+) :: (first, second) ->
  | typeof! first is \Object => angular.extend first,second
  | typeof! first is \Array => first.concat second
  | _ => first + second

  1. Code become as short as possible
  2. It will reduce of using nested functions and brackets and extra variable declarations just for better visualization
first.splice(first.index-of(second), 1)
#or
const index = 
  first.index-of(second)
first.splice(index, 1)

#It is stupid action to repeat it each time when I want to delete item from array. DAMMMN...
  1. This concept will lead to next solution for callback hell by providing workflow liked code
#not bother implementation and callbacks. Developer thinks about workflow (process) here
get-user-from-server ||> validate-user
                     ||> update-user-locally 
                     ||> send-updated-user-to-there-server

get-form-values  ||> validate-on-server
                           ||> process-result-on-client
@blvz
Copy link
Contributor

blvz commented Feb 20, 2015

I think it makes more difficult to read old code, or code from other people.

Also, #328

@vendethiel
Copy link
Contributor

Default parameters could be redefined. For instance (+) operator could be extended. by type checking of input arguments.

This is nonviable due to performance reasons.

See satyr/coco#60

As for the rest... This would be incredibly complicated due to our already insane parsing rules.

@askucher
Copy link
Author

Common, guys. There are developers around me who even don't appreciate indented syntax. This is actually why biggest weddev community don't use it. But I didn't think that it stops you. My current project is going to be converted to javascript because javascript developers even cannot read what I already done by using known livescript stuff.

About performance and insane parsing rules. I think good thinking and re-factoring can help. But I agree this is a lot of effort. But honestly I notice when I put more effort in something I really obtain valuable result. Otherwise it could be trash.

@vendethiel
Copy link
Contributor

About performance and insane parsing rules. I think good thinking and re-factoring can help. But I agree this is a lot of effort.

The performance bit is not something we can help. Having function calls instead of inlineable operators. Overriding (+) is really to be a hindrance.

@blvz
Copy link
Contributor

blvz commented Feb 20, 2015

My current project is going to be converted to javascript because javascript developers even cannot read what I already done by using known LiveScript stuff.

That's exactly my point. If people can't understand your code in LiveScript as it is now, making it obscure will just make things worse.

I had a look at your example:

get-user-from-server ||> validate-user
                     ||> update-user-locally 
                     ||> send-updated-user-to-there-server

I already know that |> is a pipe and, by looking at those functions, I guess that ||> should be some kind of async pipe. But I don't know how it works. Callbacks? Promises? Iterators? Again, I'll just guess that they are callbacks. But how errors are being handled? Also, validade-user looks like it should return a Boolean; so how update-user-locally will know about the user?

I'll have to check the operator's implementation to understand that piece of code. And that implementation would be as big as a framework. Let's take a look at the same thing, but using LiveScript's backcalls instead of custom operators:

err, usr <- get-user-from-server
return handle err if err

err, status <- validate-user usr
return handle err if err
return invalidate usr unless status

err, usr-updated <- update-user-locally usr
return handle err if err

err <- send-updated-user-to-there-server usr-updated
return handle err if err

console.log 'done.'

Just knowing how backcalls work in LiveScript, is enough for someone already familiar with coffeescript (or ruby) to understand what this is doing and it's still pretty slick.

While I agree that having something like macros can be helpful, in most situations they are not. It's our responsibility as developers (and why not, as social creatures) to consider our peers; writing readable, maintainable code is the best way to demonstrate such consideration—specially in the open source community.

Shorter isn't always better; less is less.

@blvz
Copy link
Contributor

blvz commented Feb 20, 2015

But if you really want to organize your code with "async pipes", I'm pretty sure you can write your own framework and just do this:

get-user-from-server |> validate-user
                     |> update-user-locally 
                     |> send-updated-user-to-there-server

Correct me if I'm wrong, but I think that if you implement all your methods to receive and return something like a promise, you can achieve what you want. And maybe you'll have to store values in a common object, making it obscure and hard to debug, with inevitable overrides.

@askucher
Copy link
Author

Yes. you are right

const async = (func, promise)->
  promise.success func

const get-user-from-server = ->
   const promise = Promise()
   http.get promise.subscribe
   promise


const validate = async (err, result)->
   if  result.success ...

get-user-from-server |> validate

The conclusion I didn't think good enough to find this easy concept

@blvz
Copy link
Contributor

blvz commented Feb 20, 2015

And that's fine, because you've made me give it a thought and learn more too. :)

@raine
Copy link
Contributor

raine commented Feb 20, 2015

Off-topic but could you explain the benefit of using const with functions like that?

@gabeio
Copy link

gabeio commented Feb 20, 2015

Off-topic but could you explain the benefit of using const with functions like that?

I believe that would assure that it's the same function throughout your entire app/program not allowing it to be changed anywhere else and when it goes though lsc it will yell at you if you try to.

@jampekka
Copy link

jampekka commented Apr 2, 2015

Is the design of LiveScript really so much mandated by what Javascript-programmers understand?

I'd really like see this reopened, but perhaps with something like explicit "scoped overloading". So no performance hit when not used. Eg I work a lot with vectors, so I have something like:

add = zipWith (+)
result = [1,2] `add` [3,4]

I think it would be very clear and very simple to implement eg:

+ = zipWith (+)
result = [1,2] + [3,4]

Which would be compiled to exactly as the above code, although perhaps with precedence rules enforced by parenthesizing.

And if we'd have proper "* imports", it would be very nice to do something like this

vectorStuff = () ->
    from vectorizedOperators import * # Python-like syntax
    result = [1,2] + [3,4]

Some kind of (scoped) macro system could make this also user-definable.

@ozra
Copy link

ozra commented Apr 23, 2015

I'd love custom operators, and I'd love overloading on top of that. But then LS needs to be type aware and strict on imports, and...

@vendethiel
Copy link
Contributor

Not really. maybe custom operators could be added (say with a fixed precendece to keep things simple). I think the easiest option would be to have them to be an instance call: a <*> b would be a['<*>'](b)), and we'd avoid the weird-names-in-scope (à la scala?) problem.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants