gkz / LiveScript

LiveScript is a language which compiles to JavaScript. It has a straightforward mapping to JavaScript and allows you to write expressive code devoid of repetitive boilerplate. While LiveScript adds many features to assist in functional style programming, it also has many improvements for object oriented and imperative programming.
http://livescript.net
MIT License
2.32k stars 156 forks source link

Erlang-like function notation #312

Closed dy closed 9 years ago

dy commented 11 years ago
add(a, b) ->
        a + b

Now it compiles to dubious:

add(a, b)(function(){
  return a + b;
});

It would be better to have named function instead:

function add(a, b){
 return a + b
}

Just suggestion.

Another idea: function clauses & guards. It would be super-useful to write clauses that way:

color: ->
 @get 'color'

color: (el) when el instanceof "element"  ->
 @set 'color', @parse window.getComputedStyle(prop)['background-color']

color: (prop) ->
 @set 'color', @parse prop

It complies to current syntax of guards.

apaleslimghost commented 11 years ago
:add (a,b)->
  a+b

https://github.com/satyr/coco/wiki/additions#labels-label

dy commented 11 years ago
:add (a,b)->
 a+b

add 1, 2

Compiles to:

(function add(a, b){
  return a + b;
});
add(1, 2);

Function add remains undefined in outer scope

vendethiel commented 11 years ago

Which is how function scoping works in js. You want something that "upvar"s to the highest scope?

dy commented 11 years ago

Yes, is it possible to make named function in current scope?

vendethiel commented 11 years ago

I'm not sure what you mean ?

let
 function a =>
 console.log "a ?" a?
console.log "a ?" a? 

this gives true then false

dy commented 11 years ago

Ok, I got this, thanks. I didn’t know the function a => notation, couldn’t find it in docs. But what about topic proposals?

apaleslimghost commented 11 years ago

Function guards are mostly covered by implicit switch:

color: (el)->
  | el instanceof Element => @set 'color', @parse window.getComputedStyle(prop)['background-color']
  | el? => @set 'color', @parse prop
  | otherwise => @get 'color'
dy commented 11 years ago

Yes, but it feels a bit messy to place params matching and guards in function body. Erlang-way looks clearer: there are examples in learnsomeerlang.

vendethiel commented 11 years ago

could use match

color = (el) ->
  match el
  | (instanceof Element) => ...
  | (is null) => ...
  | otherwise => ...

but I don't think that's relevant when we have almost no "types"

dy commented 11 years ago

At least it would possible to match number of arguments and value:

on: (event, cb) ->
 ...
on: (event, selector, cb) ->
 ...
on: ("hover", over, out) ->
 @on "over", over
 @on "out", out 
on: ("drag", start, drag, stop) ->
 @on "dragStart", over
 @on "drag", drag
 @on "dragStop", stop
vendethiel commented 11 years ago

We'd need to add runtime hacks in order to do that. And how do we do for dynamic names?

dy commented 11 years ago
obj[f] = (a,b) -> ...
obj[f] = (a) -> ...
method = "do"
class A
 "#method": -> ...

Ok, I see, there’s no way to make it dynamically.

dy commented 11 years ago

Another idea for pattern matching. It looks handy to have pattern mathing for objects and lists in switch, like so:

switch params
| {format:"rgba", omitAlpha:true} => @@format.rgb.toString(@model)
| {format:"rgba"} => @@format.rgba.toString(@model)
| {format:"hex", shorten} => @@formats.hex.toString(@model, shorten)
| {target in @@components, normalize = false} => @@component[target].toString(@model, normalize)
| otherwise => @@formats.rgb.toString()

It’s more readable and concise than

switch params
| params.format is "rgba" and params.omitAlpha is true => @@params.format.rgb.toString(@model)
| params.format is "rgba" => @@params.format.rgba.toString(@model)
| params.format is "hex" and params.shorten? => @@formats.hex.toString(@model, params.shorten)
| params.target in @@components => @@component[target].toString(@model, normalize)
| otherwise => @@formats.rgb.toString(@model)
vendethiel commented 11 years ago

Something like

match params<[format omitAlpha shorten target]>
| 'rgba', true, _, _ => ....
| 'rgba' , _, _, _ =>
| 'hex', _, (isnt null), _ => ....
| _, _, _, (in @components) =>
| otherwise => ...
dy commented 11 years ago

That’s really nice, thanks. Is there any docs on match and underscore?

vendethiel commented 11 years ago

Match is still experimental

dy commented 11 years ago

Seems that neither list of words nor spread work with match:

a = {c:1}
match a<[b c]>
| (?), (?) => 1
| (?), _ => 2 #=>
| _, (?) => 3
| _, _ => 4
l = [1, 2, 3]
match ...l
| _ =>

What a shame.

vendethiel commented 11 years ago

Ah, right, we need splat.

robotlolita commented 11 years ago

If the function signatures match, we could get "fake" overloaded methods by just compiling the ones in the same scope down to a giant switch — which would be an optimization in an actual overloaded method:

fib = (n) when (n == 0) -> 0
fib = (n) when (n == 1) -> 1
fib = (n) -> (fib (n - 1)) + (fib (n - 2))

Would compile down to:

fib = (n) ->
  | n == 0 => 0
  | n == 1 => 1
  | otherwise => fib(n - 1) + fib(n - 2)

This could be confusing (perhaps?), since:

head = (xs) when isArray(xs) -> xs[0]

( ... a shit load of lines afterwards, in another scope ...)
head = (xs) when isString(xs) -> xs.charAt(0)

Would compile down to:

head = (xs) ->
  | isArray(xs) => xs[0]

( ... )
head = (xs) ->
  | isString(xs) => xs.charAt(0)

At any rate, a supporting runtime for this would only be necessary if we allow these functions to be extended at runtime (e.g.: Clojure's multi-methods). Otherwise we can safely do pattern matching. The propsed syntax is not that nice, though.

vendethiel commented 11 years ago

Erlang guards only allow basic checks and some BIF (to avoid side effects). Would we take that path or not?

robotlolita commented 11 years ago

Yeah, Erlang function guards are fairly restrictive, most likely for performance reasons. Now that I've got some limited Erlang coding under my belt, I guess we could achieve the same sans the confusing semantics in my previous suggestion by adopting a new block of patterns:

map = match-fun
  (f, xs) when isArray(xs) --> [f x for x in xs]
  (f, object) when isObject(object) --> [f k, v for k, v of object]
  (f, functor) when isFunctor(object) --> functor.map f

Would compile down to something like:

var map = function() {
  var cases = [
    curry$(function (f, xs) { ... }),
    curry$(function (f, object) { ... }),
    curry$(function (f, functor) { ... })
  ]

  return function _(arg$1, arg$2) {  
    switch (false) {
      case !(arguments.length == 2 && isArray(arg$2)):
        return cases[0].apply(this, arguments)
      case !(arguments.length == 2 && isObject(arg$2)):
        return cases[1].apply(this, arguments)
      case !(arguments.length == 2 && isFunctor(arg$2)):
        return cases[2].apply(this, arguments)
      default: return _.bind(this, arguments)
    }
  }
}()

The semantics of match-fun basically boils down to:

vendethiel commented 9 years ago

I'll go ahead and close this, since this would require a "sealed"-like keyword for our functions. (which we can't really do anyway)