Example from the docs:
// Common module
package org.jetbrains.foo
expect class Foo(bar: String) {
fun frob()
}
fun main(args: Array<String>) {
Foo("Hello").frob()
}
// JVM module
package org.jetbrains.foo
actual class Foo actual constructor(val bar: String) {
actual fun frob() {
println("Frobbing the $bar")
}
}
I brought up how I thought the `expect` and `actual` keywords (but primarily the latter) added unneeded verbosity in the Kotlin Slack awhile back and was pretty much shrugged off, despite folks on the Kotlin team admitting that they exist in part to make the IDE's job easier, but weren't actually necessary.It leaves a sour taste in my mouth, but I suppose it might be because I'm familiar with projects in C/C++ where, if a symbol goes undefined, the compiler/linker is smart enough to tell you that without being hand-held.
I also believe they could have manage it without polluting the code with expect and actual.
Probably the compiler would need some extra work to do the matching.
I hear a quite a bit about it lately, but as I haven't had the chance to play with it yet, I was wondering why would one choose it over Scala.
Kotlin has gotten official support from Google, including documentation as part of Android SDK.
Scala/JVM + Scala.js has been sharing code for almost three years now, and it works perfectly out-of-the-box in Eclipse. However, in IntelliJ they barely made it kind-of work by creating an artificial `shared` module (in addition to the normal `jvm` and `js` modules) that `jvm` and `js` depend on. But `shared` cannot see the definitions from `jvm` and `js` with that model. [1]
So because IntelliJ cannot deal with shared source files, they bastardized the language to work around that limitation.
They really should have fixed the IDE instead.
Aka pragmatic.
But it can all be fixed -- you can do a hostile fork and improve upon their code together with other non-lazy developers. You'd get much more done than the Kotlin team, being non-lazy and all...
For single target code, you can prefix the class module with the platform tag (e.g. "js" or "java"). These module names are reserved within the compiler and any code therein is restricted to their respective targets.
Another way is to use conditional compilation markers to indicate specific target-compatible boundaries. E.g.
#if java
public function print(s : String) java.lang.System.out.println(v);
#end
That method only exists on the java target, and the compiler will complain otherwise.Conditional compilation also allows for more fine-grained support of multiple targets:
#if java
public function print(s : String) java.lang.System.out.println(v);
#elseif js
public function print(s : String) console.log(v); // as a simple example
#end
This makes a cleaner organizational distinction between extern code that must remain target specific, and true cross platform code that can be called on any of the supported targets. Editors can pick up on this very easily and provide early warnings as you are typing, and doc generators can also inspect these directives and show api availability between targets in the rendered docs.(self-disclosure: Haxe compiler contributor)
And your example shows that there is no way to verify that each module provides a consistent API.
Module-level distinctions are per-target. It doesn't get any more clean and clear than that, does it? The trade off is that it's a bit limiting. Conditional compilation is where you can start to pick and choose targets/features and handle more sophisticated cases.
> And your example shows that there is no way to verify that each module provides a consistent API.
This is easy to do by providing a common interface definition and a simple build class that targets each language you're interested in. It's a good idea in a formal testing suite for a complex library.
I only do that in a handful of projects though, most of the time cross-target code is just a few special exceptions. In that case, you'd call out the special case, and then use the #else directive for the rest:
#if java
public inline function print(s : String) java.lang.System.out.println(v);
#else
public inline function print(s : String) Sys.println(s); // most of the time basic ops are covered by std lib.
#end
FWIW I've "inlined" things here with the keyword. This lets you alias cross-platform methods like this without extra function call overhead.I understand JetBrains would like everyone to use InteliJ and they aren't required to support other IDEs, but it prevents adoption on many IT departments that only allow for Eclipse on their dev images.
https://kotlinlang.org/docs/tutorials/command-line.html
> "We can download it from GitHub Releases."
https://github.com/JetBrains/kotlin/releases/tag/v1.2
404
$ which kotlinc
/opt/src/kotlin-1.2.0/bin/kotlinc
$ kotlinc
/usr/bin/env: ‘bash\r’: No such file or directory
Seems to have been saved with MS Win line ends, which causes a problem with bash on Ubuntu.- server side render a page to HTML and send it to browser, or
- send down the React code as JS and render in browser, or
- use React Native to write sorta native Android/iOS apps and reuse some of my JS codebase
Does Kotlin cover the same use cases of server-side render & browser render & native mobile apps?
It forces you to start out more modular between your UI code and domain logic code, whereas React (& React Native) leaves the door open right at the start for tight coupling between UI code & domain logic, unless you expressly build this separation into your initial architecture.
Write your React code in Kotlin, compile to JS and use it everywhere where React is supported.