My Kotlin-angularjs demo is out, with some questions


#1

After several days of work, my Kotlin-angularjs demo is out there. Thanks for everyone, you have helped me a lot these days.

The demo is coming from the code from “todomvc.com”, what I have done was using Kotlin to rewrite the javascript code.

You can see it here: https://github.com/freewind/kotlin-angularjs/tree/master/src

Since I’m very new to Kotlin, I think the code can be improved, could you please review my code and help me?

And I have some questions about Kotlin-javascript compiler:

  1. Can we convert a class into a json like object in javascript? e.g.

class User(name:String, age: Int) val user = User("freewind", 100)

be compiled to:

{   name: "freewind",   age: 100 }

2. Can we ignore the parameter name when we declare a "native" function?

native fun <T> Array<T>.push(x: T) = js.noImpl native fun <T> Array<T>.splice(i1: Int, i2: Int) = js.noImpl native fun <T> Array<T>.indexOf(x: T) = js.noImpl native fun <T> Array<T>.filter(x: (T)->Boolean) = js.noImpl native fun <T> Array<T>.forEach(x: (T)->Unit) = js.noImpl native val <T> Array<T>.length: Int = js.noImpl

The “x” are unused, but I have to declare them.

  1. Can we make the function literal simpler?
{ (name:String, age: Int) -> "do some thing" }

Can we write it as:

{ name:String, age: Int -> "do some thing" }

or

(name:String, age:Int) -> "do some thing"

4. Can we specify the native name of a parameter?

{ ( native("$scope") scope: Scope, native("$timeout") timeout: Timeout) -> ... }

Which doesn't work

  1. How to declare a function literal with “vararg” ?

native fun directive(name:String, def: (vararg  Any) -> Any) = noImpl

But when I use it as:

directive("myfocus", { (scope:Any, timeout:Any) -> "" })

It can't be compiled, and the error is: Expect one parameter of type jet.Any

  1. about “$”

native("$timeout") val timeout:Timeout = noImpl

We have to write "$", is it about to just to write "$" with some specify syntax in the future?

  1. https://github.com/freewind/kotlin-angularjs/blob/master/src/services.kt#L15

return JSON.parse<String>(data!!) as Array<Todo>

Warning: This cast can never succeed. How to fix it?

  1. https://github.com/freewind/kotlin-angularjs/blob/master/src/directives.kt#L26

directive.link = { scope, elem, _attrs ->   val attrs = _attrs as Attrs

Can we just declare the parameters as "scope, elem, attrs:Boolean" in the future?

  1. remove noImpl

native var angular:Angular = noImpl

You can see there is a "noImpl" placeholder, which is not useful. Can we remove it in the future?

So we just write:

native var angular:Angular

10. Can we declare an array with type (any String parameters, with a function in the end)

angularjs.controller("Todo", ["$scope", "$timeout", function(scope, timeout) {} ])

Is it possible to declared such an type for the second parameter?

(vararg names:String, func: (vararg String) -> Any)

#2

1. Automatic serialization is not supported yet, but it will be 2. You have to specify the names 3. You can omit the parenthese only when you omit the types of parameters 4. I don't think it is supported, though we could add the support if it is needed. Why is it needed? 5. Not supported, and I don't think it will be. Why do you need it? (You can pass arrays instead) 6. Maybe, but I'm not sure 7. It's a problem in the compiler 8. We are not planning to support this 9. Unlikely. As many other "no" answers above, it would mean to tune the language to gain very little in a very specific use case. This is a bad practice: makes the language more convoluted and harder to learn. 10. You can create a class that holds a list of strings and a function, and pass its instances around.


#3

Andrey, thanks for you answer!

And I have some questions about your answers:)

2. You have to specify the names

If I have to, is it any way to let compiler not warning “it’s unused”?

3. You can omit the parenthese only when you omit the types of parameters

Why not provide this style: “{ name:String, age: Int -> “do some thing” }”?

When there are many nested literals, it’s much clearer than before:

Before:

val todoFocus = {(timeout:Timeout) ->   {(scope:Scope, elem:Elem, attrs:Attrs) ->   scope.`$watch`(attrs.todoFocus, {(newVal:Boolean) ->            if(newVal) {            timeout({ elem[0].focus() }, 0, false)            }   }   } }

After:

val todoFocus = { timeout:Timeout  ->   { scope:Scope, elem:Elem, attrs:Attrs  ->   scope.`$watch`(attrs.todoFocus, { newVal:Boolean  ->            if(newVal) {            timeout({ elem[0].focus() }, 0, false)            }   }   } }

val todoFocus = (timeout:Timeout) -> {
  (scope:Scope, elem:Elem, attrs:Attrs) -> {
  scope.$watch(attrs.todoFocus, (newVal:Boolean) ->{
           if(newVal) {
           timeout({ elem[0].focus() }, 0, false)
           }
  }
  }
}


4. I don’t think it is supported, though we could add the support if it is needed. Why is it needed?

Because some js library are reading the parameter names and inject some services to the function.

The correct parameter names are “$scope”, “timeout", but I can't define such paramter names since there is a "” in the name.

5. Not supported, and I don’t think it will be. Why do you need it? (You can pass arrays instead)

I haven’t try the “array” version now.

I need it because some functions can accept zero or more parameters.

e.g.

angular.directive(name:String, def: (zero or more injections) -> Any)

It's nature to declare it as (vararg Any)

Then I can invoke it no matter how many paramters are there:

angular.directive("todoFocus", { "..." }) angular.directive("todoFocus", { (a:String) -> "..." }) angular.directive("todoFocus", { (a:String, b:String) -> "..." }) angular.directive("todoFocus", { (a:String, b:String, c:String) -> "..." })

10. You can create a class that holds a list of strings and a function, and pass its instances around.

Could you give me an example? How to create such a class?


#4

2. Supressing warning is not supported yet, but will be 3. Because the complete syntax is {<parameters> : <return type> -> <body>} 4. Please file an issue with a detailed description of the problem 5. Are you sure that JavaScript will pass these injections as a vararg? 10. class C(val l: List<String>, val f: () -> Unit)


#5

Just create an feature request for 4: http://youtrack.jetbrains.com/issue/KT-3643

  1. I’m not sure if we are talking about the same thing, let me take an example, it’s how angularjs’ “directive” working(If I don’t understand incorrect):

There is a js function:

function directive(myfun) {   // inspect the source code of myfun   // use regex to capture parameter names, it can be 0 or many   // get corresponding services from factory per parameter names   // invoke: myfun(services) }

You can see we can pass any number of "names" to this function:

directive(function(){ ... }); directive(function($scope) {}); directive(function($scope, $timeout){});

Now I want to write a Kotlin function for it:

native fun directive(myfun: (vararg names: String)->Any): Any = noImpl

directive({ “working” })
directive({ (a:String) -> “working” })
directive({ (a:String, b: String) -> “doesn’t work” }) // !!! can’t be compiled


How to fix it?

  1. Here is a js function provided by angularjs:

function directive(name, injectsAndFun) { }

When can invoke it as:

directive("myfocus", ["$scope", "$timeout", function(scope, timeout) {   // if we pass an array, then we can put injection names as strings on the beginning, and a function on the end,   // which use can custom names and will be injected with correct service objects because we   // have passed the correct names in the array }]);

You can see the second parameter of function "directive" is an array, and it has several string names, and ends with a function.

Is it possible to define some types for such kind of array? ( I guess not  :p)

For now, I defined a native function as:

native fun directive(name:String, injectsAndFun: Array<Any>)

You can see the second one is an array of Any, the compiler won't help us to check if it starts with some strings, and ends with a function.

I tried your code to define such a class “class C(val l: List<String>, val f: () -> Unit)” and pass it to the “directive”:

val c = C(array("aaa", "bbb"), { (aaa:String, bbb:String) -> println("ss") }) directive("myfocus", c)

But it’s not working as I expected.


#6

5. I don't think your abstraction is correct: you want a function that accepts _any funciton_, not only functions taking a vararg. Kotlin doesn't have an "any function" type at the moment, although maybe it makes sense to have one. As a workaround, you can create several overloads taking functions with different numbers of parameters

  1. This can’t be done in any clean way (unless we add something very angular-specific to the language).