├── .gitignore ├── LICENSE ├── README.md ├── code ├── arrays.cs ├── arrays.kt ├── casting.cs ├── casting.kt ├── checking-type.cs ├── checking-type.kt ├── class-declaration.cs ├── class-declaration.kt ├── collections.cs ├── collections.kt ├── compiletime-constants.cs ├── compiletime-constants.kt ├── conditional.cs ├── conditional.kt ├── coroutines-tasks.cs ├── coroutines-tasks.kt ├── data-class.cs ├── data-class.kt ├── default-arguments.cs ├── default-arguments.kt ├── dynamic.cs ├── dynamic.kt ├── empty-collections.cs ├── empty-collections.kt ├── exception-expression.cs ├── exception-expression.kt ├── exceptions.cs ├── exceptions.kt ├── explicit-types.cs ├── explicit-types.kt ├── extension-methods.cs ├── extension-methods.kt ├── foreach.cs ├── foreach.kt ├── functions.cs ├── functions.kt ├── generic-conditional.cs ├── generic-conditional.kt ├── hello-world.cs ├── hello-world.kt ├── hof-parameter.cs ├── hof-parameter.kt ├── hof-return.cs ├── hof-return.kt ├── if-statement.cs ├── if-statement.kt ├── immutable-data-class.cs ├── immutable-data-class.kt ├── inclusive-range-operator.cs ├── inclusive-range-operator.kt ├── interface.cs ├── interface.kt ├── lambda.cs ├── lambda.kt ├── lists.cs ├── lists.kt ├── map.cs ├── map.kt ├── maps.cs ├── maps.kt ├── named-arguments.cs ├── named-arguments.kt ├── null-coalescing.cs ├── null-coalescing.kt ├── null-conditional.cs ├── null-conditional.kt ├── null-reference.cs ├── null-reference.kt ├── pattern-matching-2.cs ├── pattern-matching-2.kt ├── pattern-matching.cs ├── pattern-matching.kt ├── range-and-index.cs ├── range-and-index.kt ├── range-operator.cs ├── range-operator.kt ├── sequence.cs ├── sequence.kt ├── single-expression.cs ├── single-expression.kt ├── smart-casting.cs ├── smart-casting.kt ├── sort.cs ├── sort.kt ├── string-interpolation.cs ├── string-interpolation.kt ├── subclass.cs ├── subclass.kt ├── test-code.csx ├── test-code.kts ├── tuple-return.cs ├── tuple-return.kt ├── type-coercion.cs ├── type-coercion.kt ├── usage.cs ├── usage.kt ├── variable-number-of-arguments.cs ├── variable-number-of-arguments.kt ├── variables-and-constants.cs └── variables-and-constants.kt ├── css └── style.css ├── index.cirru ├── index.html ├── make.coffee ├── package-lock.json └── package.json /.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | .DS_Store 3 | *~ 4 | \#*# 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Tomi Tuhkanen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | # Kotlin is like C# 3 | 4 | Page: https://ttu.github.io/kotlin-is-like-csharp/ 5 | 6 | Programmatically inspired from [kotlin-is-like-typescript](https://github.com/gi-no/kotlin-is-like-typescript), [swift-is-like-kotlin](https://github.com/Nilhcem/swift-is-like-kotlin), [swift-is-like-go](https://github.com/jiyinyiyong/swift-is-like-go) and visually inspired from [swiftislikescala](https://github.com/leverich/swiftislikescala) 7 | 8 | ### Contributing 9 | 10 | Fixes, improvents and additions are welcome. Open an issue or a PR. 11 | 12 | ### License 13 | 14 | MIT 15 | 16 | ### Develop 17 | 18 | ```bash 19 | # install dependencies to build tools 20 | $ npm i 21 | # build html 22 | $ npm run build 23 | ``` 24 | 25 | HTML is generated from `index.cirru`. -------------------------------------------------------------------------------- /code/arrays.cs: -------------------------------------------------------------------------------- 1 | var shoppingList = new[] { "catfish", "water", 2 | "tulips", "blue paint" }; 3 | shoppingList[1] = "bottle of water"; -------------------------------------------------------------------------------- /code/arrays.kt: -------------------------------------------------------------------------------- 1 | val shoppingList = arrayOf("catfish", "water", 2 | "tulips", "blue paint") 3 | shoppingList[1] = "bottle of water" 4 | -------------------------------------------------------------------------------- /code/casting.cs: -------------------------------------------------------------------------------- 1 | // Unsafe (throw exception) 2 | var circle = (Circle)shape; 3 | 4 | // Safe (return null) 5 | var circle = shape as Circle; 6 | 7 | // If Nullable reference types enabled (optional feature) 8 | Circle? circle = shape as Circle; -------------------------------------------------------------------------------- /code/casting.kt: -------------------------------------------------------------------------------- 1 | // Unsafe (throw exception) 2 | val circle: Circle = shape as Circle 3 | 4 | // Safe (return null) 5 | val circle: Circle? = shape as Circle? 6 | 7 | val circle: Circle? = shape as? Circle -------------------------------------------------------------------------------- /code/checking-type.cs: -------------------------------------------------------------------------------- 1 | var movieCount = 0; 2 | var songCount = 0; 3 | 4 | foreach (var item in library) 5 | { 6 | if (item is Movie) 7 | ++movieCount; 8 | else if (item is Song) 9 | ++songCount; 10 | } 11 | -------------------------------------------------------------------------------- /code/checking-type.kt: -------------------------------------------------------------------------------- 1 | var movieCount = 0 2 | var songCount = 0 3 | 4 | for (item in library) { 5 | if (item is Movie) { 6 | ++movieCount 7 | } else if (item is Song) { 8 | ++songCount 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /code/class-declaration.cs: -------------------------------------------------------------------------------- 1 | class Shape 2 | { 3 | public int NumberOfSides { get; set; } 4 | public string SimpleDescription() => 5 | $"A shape with {NumberOfSides} sides."; 6 | } 7 | -------------------------------------------------------------------------------- /code/class-declaration.kt: -------------------------------------------------------------------------------- 1 | class Shape { 2 | var numberOfSides = 0 3 | fun simpleDescription() = 4 | "A shape with $numberOfSides sides." 5 | } 6 | -------------------------------------------------------------------------------- /code/collections.cs: -------------------------------------------------------------------------------- 1 | var datas = new List<SensorData> 2 | { 3 | new SensorData { Id = 1, Location = "A", Value = 2.89 }, 4 | new SensorData { Id = 2, Location = "B", Value = 12.01 }, 5 | new SensorData { Id = 3, Location = "B", Value = 11.89 }, 6 | new SensorData { Id = 4, Location = "A", Value = 3.11 }, 7 | new SensorData { Id = 5, Location = "A", Value = -456.0 } 8 | }; 9 | 10 | var avgs = datas 11 | .Where(e => e.Value > -50.0) 12 | .GroupBy(e => e.Location) 13 | .Select(g => new { 14 | Location = g.Key, 15 | Value = g.Average(e => e.Value) }); 16 | 17 | // { Location = A, Value = 3.0 } 18 | // { Location = B, Value = 11.95 } -------------------------------------------------------------------------------- /code/collections.kt: -------------------------------------------------------------------------------- 1 | val datas = listOf( 2 | SensorData(1, "A", 2.89), 3 | SensorData(2, "B", 12.01), 4 | SensorData(3, "B", 11.89), 5 | SensorData(4, "A", 3.11), 6 | SensorData(5, "A", -456.0) 7 | ) 8 | 9 | val avgs = datas 10 | .filter { it.value > -50.0 } 11 | .groupBy(SensorData::location) 12 | .map { Location(it.key, it.value.map(SensorData::value).average()) } 13 | 14 | // (location=A, value=3.0) 15 | // (location=B, value=11.95) -------------------------------------------------------------------------------- /code/compiletime-constants.cs: -------------------------------------------------------------------------------- 1 | const string SYSTEM_DEPRECATED = "System is deprecated"; -------------------------------------------------------------------------------- /code/compiletime-constants.kt: -------------------------------------------------------------------------------- 1 | const val SYSTEM_DEPRECATED: String = "System is deprecated" -------------------------------------------------------------------------------- /code/conditional.cs: -------------------------------------------------------------------------------- 1 | var loaded = true; 2 | var status = loaded ? "Ready" : "Loading..."; 3 | // "Ready -------------------------------------------------------------------------------- /code/conditional.kt: -------------------------------------------------------------------------------- 1 | // if is an expression, so ternary operation not needed 2 | val loaded = true 3 | val status = if (loaded) "Ready" else "Loading..." 4 | // "Ready -------------------------------------------------------------------------------- /code/coroutines-tasks.cs: -------------------------------------------------------------------------------- 1 | var client = new HttpClient(); 2 | 3 | var repos = new [] { "jetbrains/kotlin", "dotnet/csharplang" }; 4 | 5 | var asyncRequests = repos.Select(async repo => 6 | { 7 | var response = await client.GetAsync($"https://api.github.com/repos/{repo}"); 8 | var json = await response.Content.ReadAsStringAsync(); 9 | dynamic content = JsonConvert.DeserializeObject(json); 10 | return new { repo, stars = content.stargazers_count, forks = content.forks }; 11 | }); 12 | 13 | var results = await Task.WhenAll(asyncRequests); 14 | 15 | foreach(var data in results) 16 | Console.WriteLine($"{data.repo} : {data.stars} - {data.forks}"); -------------------------------------------------------------------------------- /code/coroutines-tasks.kt: -------------------------------------------------------------------------------- 1 | data class Stats(val full_name: String, val stargazers_count: Int = -1, val forks: Int = -1) 2 | 3 | val mapper = jacksonObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) 4 | 5 | val repos = listOf("jetbrains/kotlin", "dotnet/csharplang") 6 | 7 | val asyncRequests = repos.map { repo -> 8 | GlobalScope.async { 9 | val body = Fuel.get("https://api.github.com/repos/$repo") 10 | .responseString() 11 | .third.component1() // Fuel Result & Body 12 | body?.let { mapper.readValue(it) } ?: Stats(repo) 13 | } 14 | } 15 | 16 | runBlocking { 17 | val results = asyncRequests.map { it.await() } 18 | results.forEach{ println("${it.full_name} : ${it.stargazers_count} - ${it.forks}") } 19 | } -------------------------------------------------------------------------------- /code/data-class.cs: -------------------------------------------------------------------------------- 1 | // Pre C# 9 doesn't have data classes 2 | public class Customer 3 | { 4 | public int Id { get; set; } 5 | public string Name { get; set; } 6 | } 7 | 8 | var customer = new Customer { Id = 1, Name = "Sachin" }; 9 | 10 | var name = customer.Name; 11 | customer.Id = 2 12 | 13 | // C# 9 has records 14 | // Records can be mutable, but they are primarily 15 | // intended for supporting immutable data models 16 | public record Customer 17 | { 18 | public int Id { get; set; } 19 | public string Name { get; set; } 20 | } -------------------------------------------------------------------------------- /code/data-class.kt: -------------------------------------------------------------------------------- 1 | data class Customer(var id: Long, var name: String) 2 | 3 | val customer = Customer(1, "Sachin") 4 | 5 | val name = customer.name 6 | customer.id = 2 7 | -------------------------------------------------------------------------------- /code/default-arguments.cs: -------------------------------------------------------------------------------- 1 | void DisplayGreeting(string message, string name = "Guest") 2 | { 3 | Console.WriteLine($"Hello {name}, {message}"); 4 | } 5 | 6 | DisplayGreeting("welcome!"); 7 | // Hello Guest, welcome! -------------------------------------------------------------------------------- /code/default-arguments.kt: -------------------------------------------------------------------------------- 1 | fun displayGreeting(message: String, name: String = "Guest") { 2 | println("Hello $name, $message") 3 | } 4 | 5 | displayGreeting("welcome!") 6 | // Hello Guest, welcome! -------------------------------------------------------------------------------- /code/dynamic.cs: -------------------------------------------------------------------------------- 1 | /* with dynamic, type is not known until runtime */ 2 | 3 | var json = @"[ 4 | { 'id': 'A', 'work': { 'name': 'ACME 2', 'location': 'NY' } }, 5 | { 'id': 'B', 'work': { 'name': 'Box Co', 'location': 'SF' } }, 6 | { 'id': 'C', 'work': { 'name': 'DotCom', 'location': 'NY' } } 7 | ]"; 8 | 9 | var users = JsonConvert.DeserializeObject<List<dynamic>>(json); 10 | 11 | var name = users.First().work.name; 12 | // ACME 2 13 | 14 | var fromNY = users 15 | .Where(e => e.work.location == "NY") 16 | .Select(e => e.id); 17 | // [A, C] -------------------------------------------------------------------------------- /code/dynamic.kt: -------------------------------------------------------------------------------- 1 | // The dynamic type is not supported in code targeting the JVM 2 | // https://kotlinlang.org/docs/reference/dynamic-type.html 3 | 4 | // JSON example with data classes 5 | data class Work(val name: String, val location: String) 6 | data class User(val id: String, val work: Work) 7 | 8 | val json = """[ 9 | { "id": "A", "work": { "name": "ACME 2", "location": "NY" } }, 10 | { "id": "B", "work": { "name": "Box Co", "location": "SF" } }, 11 | { "id": "C", "work": { "name": "DotCom", "location": "NY" } } 12 | ]""" 13 | 14 | val users = jacksonObjectMapper().readValue<List<User>>(json) 15 | 16 | val name = users.first().work.name 17 | // ACME 2 18 | 19 | val fromNy = users 20 | .filter { it.work.location == "NY" } 21 | .map { it.id } 22 | // [A, C] -------------------------------------------------------------------------------- /code/empty-collections.cs: -------------------------------------------------------------------------------- 1 | var emptyList = new List<string>(); 2 | var emptyDictionary = new Dictionary<string, float>(); 3 | 4 | // read-only empty list 5 | var empty = Enumerable.Empty<string>(); -------------------------------------------------------------------------------- /code/empty-collections.kt: -------------------------------------------------------------------------------- 1 | val emptyList = mutableListOf<String>() 2 | val emptyMap = mutableMapOf<String, Float>() 3 | 4 | // read-only empty list 5 | val empty = emptyList<String>() -------------------------------------------------------------------------------- /code/exception-expression.cs: -------------------------------------------------------------------------------- 1 | // try is not an expression 2 | int? a; 3 | try { a = int.Parse(input); } 4 | catch { a = null; } -------------------------------------------------------------------------------- /code/exception-expression.kt: -------------------------------------------------------------------------------- 1 | // try is an expression, i.e., it may have a return value 2 | val a: Int? = try { input.toInt() } 3 | catch (e: NumberFormatException) { null } -------------------------------------------------------------------------------- /code/exceptions.cs: -------------------------------------------------------------------------------- 1 | try 2 | { 3 | // Some code 4 | } 5 | catch (SomeException e) when (e.SomeCode == 404) 6 | { 7 | // Handle SomeException only when SomeCode is 404 8 | } 9 | catch (SomeException e) 10 | { 11 | // Handle SomeException 12 | } 13 | catch (Exception e) 14 | { 15 | // Handle rest of the Exceptions 16 | } 17 | finally 18 | { 19 | // Optional finally block 20 | } -------------------------------------------------------------------------------- /code/exceptions.kt: -------------------------------------------------------------------------------- 1 | try { 2 | // some code 3 | } 4 | catch (e: SomeException) { 5 | if (e.SomeCode == 404) { 6 | // handle SomeException when SomeCode is 404 7 | } else { 8 | // handle SomeException 9 | } 10 | } 11 | catch (e: Exception) { 12 | // handle rest of the Exceptions 13 | } 14 | finally { 15 | // optional finally block 16 | } -------------------------------------------------------------------------------- /code/explicit-types.cs: -------------------------------------------------------------------------------- 1 | double explicitDouble = 70.0; 2 | -------------------------------------------------------------------------------- /code/explicit-types.kt: -------------------------------------------------------------------------------- 1 | val explicitDouble: Double = 70.0 2 | -------------------------------------------------------------------------------- /code/extension-methods.cs: -------------------------------------------------------------------------------- 1 | public static class Extensions 2 | { 3 | public static void Swap(this List<int> list, int idx1, int idx2) 4 | { 5 | var temp = list[idx1]; 6 | list[idx1] = list[idx2]; 7 | list[idx2] = temp; 8 | } 9 | } 10 | 11 | var list = new List<int> { 1, 5, 3 }; 12 | list.Swap(0, 2); 13 | // [ 3, 5, 1 ] 14 | -------------------------------------------------------------------------------- /code/extension-methods.kt: -------------------------------------------------------------------------------- 1 | fun MutableList<Int>.swap(idx1: Int, idx2: Int) { 2 | val tmp = this[idx1] 3 | this[idx1] = this[idx2] 4 | this[idx2] = tmp 5 | } 6 | 7 | val list = mutableListOf(1, 5, 3) 8 | list.swap(0, 2) 9 | // [ 3, 5, 1 ] -------------------------------------------------------------------------------- /code/foreach.cs: -------------------------------------------------------------------------------- 1 | var names = new List<string> { "Anna", "Alex", "Brian", "Jack" }; 2 | 3 | foreach (var name in names) 4 | { 5 | Console.WriteLine($"Person is called {name}"); 6 | } 7 | 8 | names.ForEach(name => Console.WriteLine($"Person is called {name}")); 9 | 10 | // Person is called Anna 11 | // Person is called Alex 12 | // Person is called Brian 13 | // Person is called Jack -------------------------------------------------------------------------------- /code/foreach.kt: -------------------------------------------------------------------------------- 1 | val names = arrayOf("Anna", "Alex", "Brian", "Jack") 2 | 3 | for (name in names) { 4 | println("Person is called $name") 5 | } 6 | 7 | names.forEach { println("Person is called $it") } 8 | 9 | // Person is called Anna 10 | // Person is called Alex 11 | // Person is called Brian 12 | // Person is called Jack -------------------------------------------------------------------------------- /code/functions.cs: -------------------------------------------------------------------------------- 1 | string Greet(string name, string day) 2 | { 3 | return $"Hello {name}, today is {day}."; 4 | } 5 | 6 | var text = Greet("Bob", "Tuesday"); 7 | // Hello Bob, today is Tuesday -------------------------------------------------------------------------------- /code/functions.kt: -------------------------------------------------------------------------------- 1 | fun greet(name: String, day: String): String { 2 | return "Hello $name, today is $day." 3 | } 4 | 5 | val text = greet("Bob", "Tuesday") 6 | // Hello Bob, today is Tuesday. -------------------------------------------------------------------------------- /code/generic-conditional.cs: -------------------------------------------------------------------------------- 1 | // Generic helper method that will return boolean and set output 2 | bool GetValue<T>(T input, out T output) 3 | { 4 | output = input; 5 | return output != null; 6 | } 7 | 8 | var data = new DataPoint 9 | { 10 | Id = 1, 11 | Celsius = 22.1, 12 | Child = new DataPoint { Id = 2, Celsius = 22.8 } 13 | }; 14 | 15 | var result = GetValue(data.Child?.Child, out var output) 16 | ? ToFahrenheit(output.Celsius) 17 | : double.MinValue; 18 | 19 | string set = "My text"; 20 | var text = GetValue(set, out var output) ? output : "Not set"; 21 | // "My text" 22 | string notSet = null; 23 | var text = GetValue(notSet, out var output) ? output : "Not set"; 24 | // "Not set" 25 | -------------------------------------------------------------------------------- /code/generic-conditional.kt: -------------------------------------------------------------------------------- 1 | // Use .let and forget weird helper methods 2 | 3 | val data = DataPoint(1, 22.1, DataPoint(2, 22.8)) 4 | 5 | val result = data.child?.child?. 6 | let { toFahrenheit(it.celsius) } ?: Double.MIN_VALUE -------------------------------------------------------------------------------- /code/hello-world.cs: -------------------------------------------------------------------------------- 1 | public void Main() 2 | { 3 | Console.WriteLine("Hello, world!"); 4 | } 5 | 6 | // C# 9 supports top-level statements 7 | Console.WriteLine("Hello, world!"); -------------------------------------------------------------------------------- /code/hello-world.kt: -------------------------------------------------------------------------------- 1 | println("Hello, world!") 2 | -------------------------------------------------------------------------------- /code/hof-parameter.cs: -------------------------------------------------------------------------------- 1 | string Transform(string initial, Func<string, string> f) => f(initial); 2 | 3 | var result = Transform("hello", x => x.ToUpper()); 4 | // HELLO -------------------------------------------------------------------------------- /code/hof-parameter.kt: -------------------------------------------------------------------------------- 1 | fun transform(initial: String, f: (String) -> String) = f(initial) 2 | 3 | val result = transform("hello", { x -> x.toUpperCase() }) 4 | // HELLO 5 | 6 | // Trailing lambda can be placed outside the parentheses 7 | val result2 = transform("hello") { x -> x.toUpperCase() } -------------------------------------------------------------------------------- /code/hof-return.cs: -------------------------------------------------------------------------------- 1 | Func<int, int> MakeIncrementer() 2 | { 3 | int addOne(int number) => 1 + number; 4 | return addOne; 5 | } 6 | 7 | var increment = MakeIncrementer(); 8 | var result = increment(7); 9 | 10 | // MakeIncrementer can also be written in a shorter way: 11 | Func<int, int> MakeIncrementer() => i => 1 + i; 12 | -------------------------------------------------------------------------------- /code/hof-return.kt: -------------------------------------------------------------------------------- 1 | fun makeIncrementer(): (Int) -> Int { 2 | val addOne = fun(number: Int): Int { 3 | return 1 + number 4 | } 5 | return addOne 6 | } 7 | 8 | val increment = makeIncrementer() 9 | val result = increment(7) 10 | 11 | // makeIncrementer can also be written in a shorter way: 12 | fun makeIncrementer() = fun(number: Int) = 1 + number 13 | -------------------------------------------------------------------------------- /code/if-statement.cs: -------------------------------------------------------------------------------- 1 | var age = 42; 2 | 3 | if (age < 10) 4 | { 5 | Console.WriteLine("You're too young to watch this movie"); 6 | } 7 | else if (age < 13) 8 | { 9 | Console.WriteLine("You can watch this movie with a parent"); 10 | } 11 | else 12 | { 13 | Console.WriteLine("You can watch this movie"); 14 | } -------------------------------------------------------------------------------- /code/if-statement.kt: -------------------------------------------------------------------------------- 1 | val age = 42 2 | 3 | if (age < 10) { 4 | println("You're too young to watch this movie") 5 | } else if (age < 13) { 6 | println("You can watch this movie with a parent") 7 | } else { 8 | println("You can watch this movie") 9 | } -------------------------------------------------------------------------------- /code/immutable-data-class.cs: -------------------------------------------------------------------------------- 1 | // C# 9 immutable records can be created with positional parameters 2 | public record Customer(int Id, string Name); 3 | 4 | Customer customer = new(1, "Sachin"); 5 | customer.Id = 2 // Error 6 | 7 | // or with standard property syntax and init only setter 8 | public record Customer 9 | { 10 | public int Id { get; init; } 11 | public string Name { get; init; } 12 | }; 13 | 14 | var customer = new Customer { Id = 1, Name = "Sachin" }; 15 | customer.Id = 2 // Error 16 | 17 | // Pre C# 9 18 | public class Customer 19 | { 20 | public Customer(int id, string name) => (Id, Name) = (id, name); 21 | 22 | public int Id { get; } 23 | public string Name { get; } 24 | } 25 | 26 | var customer = new Customer(1, "Sachin"); 27 | 28 | var name = customer.Name; 29 | customer.Id = 2 // Error -------------------------------------------------------------------------------- /code/immutable-data-class.kt: -------------------------------------------------------------------------------- 1 | data class Customer(val id: Long, val name: String) 2 | 3 | val customer = Customer(1, "Sachin") 4 | 5 | val name = customer.name 6 | customer.id = 2 // Error 7 | -------------------------------------------------------------------------------- /code/inclusive-range-operator.cs: -------------------------------------------------------------------------------- 1 | foreach (var index in Enumerable.Range(1, 5)) 2 | { 3 | Console.WriteLine($"{index} times 5 is {index * 5}"); 4 | } 5 | // 1 times 5 is 5 6 | // 2 times 5 is 10 7 | // 3 times 5 is 15 8 | // 4 times 5 is 20 9 | // 5 times 5 is 25 10 | -------------------------------------------------------------------------------- /code/inclusive-range-operator.kt: -------------------------------------------------------------------------------- 1 | for (index in 1..5) { 2 | println("$index times 5 is ${index * 5}") 3 | } 4 | // 1 times 5 is 5 5 | // 2 times 5 is 10 6 | // 3 times 5 is 15 7 | // 4 times 5 is 20 8 | // 5 times 5 is 25 9 | -------------------------------------------------------------------------------- /code/interface.cs: -------------------------------------------------------------------------------- 1 | interface INameable 2 | { 3 | string Name(); 4 | } 5 | 6 | void GenericMethod<T>(T x) where T : INameable 7 | { 8 | Console.WriteLine("Name is " + x.Name()); 9 | } 10 | 11 | class Person : INameable 12 | { 13 | public string Name() => "Person A"; 14 | } -------------------------------------------------------------------------------- /code/interface.kt: -------------------------------------------------------------------------------- 1 | interface Nameable { 2 | fun name(): String 3 | } 4 | 5 | fun <T: Nameable> genericFunction(x: T) { 6 | println("Name is " + x.name()) 7 | } 8 | 9 | class Person : Nameable { 10 | override fun name() = "Person A" 11 | } 12 | -------------------------------------------------------------------------------- /code/lambda.cs: -------------------------------------------------------------------------------- 1 | bool ContainsEven(List<int> numbers) => numbers.Any(e => e % 2 == 0); -------------------------------------------------------------------------------- /code/lambda.kt: -------------------------------------------------------------------------------- 1 | fun containsEven(numbers: List<Int>) = numbers.any { it % 2 == 0 } -------------------------------------------------------------------------------- /code/lists.cs: -------------------------------------------------------------------------------- 1 | IReadOnlyList<string> shoppingList = new List<string> { "catfish", 2 | "water", "tulips", "blue paint" }; 3 | 4 | var shoppingListMutable = new List<string> { "catfish", "water", 5 | "tulips", "blue paint" }; 6 | shoppingListMutable[1] = "bottle of water"; 7 | shoppingListMutable.Add("bucket"); -------------------------------------------------------------------------------- /code/lists.kt: -------------------------------------------------------------------------------- 1 | val shoppingList = listOf("catfish", "water", 2 | "tulips", "blue paint") 3 | 4 | val shoppingListMutable = mutableListOf("catfish", "water", 5 | "tulips", "blue paint") 6 | shoppingListMutable[1] = "bottle of water" 7 | shoppingListMutable.add("bucket") 8 | -------------------------------------------------------------------------------- /code/map.cs: -------------------------------------------------------------------------------- 1 | var numbers = new[] { 20, 19, 7, 12 }; 2 | var multiplied = numbers.Select(e => 3 * e); 3 | // [ 60, 57, 21, 36 ] -------------------------------------------------------------------------------- /code/map.kt: -------------------------------------------------------------------------------- 1 | val numbers = listOf(20, 19, 7, 12) 2 | val multiplied = numbers.map { 3 * it } 3 | // [ 60, 57, 21, 36 ] 4 | -------------------------------------------------------------------------------- /code/maps.cs: -------------------------------------------------------------------------------- 1 | IReadOnlyDictionary<string,string> occupations = 2 | new Dictionary<string, string> 3 | { 4 | ["Malcolm"] = "Captain", 5 | ["Kaylee"] = "Mechanic" 6 | }; 7 | 8 | var occupationsMutable = new Dictionary<string, string> 9 | { 10 | ["Malcolm"] = "Captain", 11 | ["Kaylee"] = "Mechanic" 12 | }; 13 | occupationsMutable["Jayne"] = "Public Relations"; 14 | occupationsMutable.Add("Rick", "Navigation"); 15 | -------------------------------------------------------------------------------- /code/maps.kt: -------------------------------------------------------------------------------- 1 | val occupations = mapOf( 2 | "Malcolm" to "Captain", 3 | "Kaylee" to "Mechanic" 4 | ) 5 | 6 | val occupationsMutable = mutableMapOf( 7 | "Malcolm" to "Captain", 8 | "Kaylee" to "Mechanic" 9 | ) 10 | occupationsMutable["Jayne"] = "Public Relations" 11 | occupationsMutable.put("Rick", "Navigation") 12 | -------------------------------------------------------------------------------- /code/named-arguments.cs: -------------------------------------------------------------------------------- 1 | int Area(int width, int height) => width * height; 2 | var result = Area(width: 2, height: 3); 3 | 4 | // This is also possible with named arguments 5 | result = Area(2, height: 2); 6 | result = Area(height: 3, width: 2); -------------------------------------------------------------------------------- /code/named-arguments.kt: -------------------------------------------------------------------------------- 1 | fun area(width: Int, height: Int) = width * height 2 | var result = area(width = 2, height = 3) 3 | 4 | // This is also possible with named arguments 5 | result = area(2, height = 2) 6 | result = area(height = 3, width = 2) 7 | -------------------------------------------------------------------------------- /code/null-coalescing.cs: -------------------------------------------------------------------------------- 1 | var data = new DataPoint 2 | { 3 | Id = 1, 4 | Celsius = 22.1, 5 | Child = new DataPoint { Id = 2, Celsius = 22.8 } 6 | }; 7 | 8 | var result = data.Child?.Child?.Celsius ?? double.MinValue; 9 | // double.MinValue -------------------------------------------------------------------------------- /code/null-coalescing.kt: -------------------------------------------------------------------------------- 1 | val data = DataPoint(1, 22.1, DataPoint(2, 22.8)) 2 | 3 | val result = data.child?.child?.celsius ?: Double.MIN_VALUE 4 | // Double.MIN_VALUE -------------------------------------------------------------------------------- /code/null-conditional.cs: -------------------------------------------------------------------------------- 1 | public class DataPoint 2 | { 3 | public int Id { get; set; } 4 | public double Celsius { get; set; } 5 | public DataPoint? Child { get; set; } 6 | } 7 | 8 | var data = new DataPoint 9 | { 10 | Id = 1, 11 | Celsius = 22.1, 12 | Child = new DataPoint { Id = 2, Celsius = 22.8 } 13 | }; 14 | 15 | var result = data.Child?.Child != null 16 | ? ToFahrenheit(data.Child.Child.Celsius) 17 | : double.MinValue; -------------------------------------------------------------------------------- /code/null-conditional.kt: -------------------------------------------------------------------------------- 1 | data class DataPoint(val id: Int, val celsius: Double, 2 | val child: DataPoint? = null) 3 | 4 | val data = DataPoint(1, 22.1, DataPoint(2, 22.8)) 5 | 6 | val result = data.child?.child?. 7 | let { toFahrenheit(it.celsius) } ?: Double.MIN_VALUE -------------------------------------------------------------------------------- /code/null-reference.cs: -------------------------------------------------------------------------------- 1 | // Nullable reference types are optional feature in C# 2 | 3 | public class Measurement 4 | { 5 | public double Celsius { get; set; } 6 | } 7 | 8 | Measurement? data = null; // Ok: can be null 9 | Measurement data = null; // Error: can't be null 10 | 11 | void PrintMayBeNull(Measurement? data) 12 | { 13 | // data can be null, must have null check 14 | if (data == null) 15 | return; 16 | 17 | Console.WriteLine(data.Celsius); 18 | } 19 | 20 | void PrintNoNull(Measurement data) 21 | { 22 | // data can't be null. No need for check 23 | Console.WriteLine(data.Celsius); 24 | } 25 | 26 | Measurement? current = GetDataFromApi(); 27 | 28 | PrintMayBeNull(current); // No need for check as method accepts nulls 29 | 30 | if (current == null) 31 | return; 32 | 33 | PrintNoNull(current); // OK: Null is checked before method call -------------------------------------------------------------------------------- /code/null-reference.kt: -------------------------------------------------------------------------------- 1 | data class Measurement(val celsius: Double) 2 | 3 | val data: Measurement = null // Error: can't be null 4 | val data: Measurement? = null // Ok: can be null 5 | 6 | fun printMayBeNull(data: Measurement?) { 7 | // data can be null, must have null check 8 | if (data == null) 9 | return 10 | 11 | println(data.celsius) 12 | } 13 | 14 | fun printNoNull(data: Measurement) { 15 | // data can't be null. No need for check 16 | println(data.celsius) 17 | } 18 | 19 | val current: Measurement? = getDataFromApi() 20 | 21 | printMayBeNull(current) // Ok: can be null 22 | 23 | if (current == null) 24 | return 25 | 26 | printNoNull(current) 27 | -------------------------------------------------------------------------------- /code/pattern-matching-2.cs: -------------------------------------------------------------------------------- 1 | var result = item switch 2 | { 3 | Square s => Handle(s), 4 | Circle { Radius: < 10 } c => HandleUnder10(c), 5 | Circle { Radius: 20 } c => Handle20(c), 6 | Circle c => Handle(c), 7 | _ => throw new Exception("Unknown shape") 8 | }; 9 | 10 | // Same with if statements 11 | if (item is Square s) 12 | { } 13 | else if (item is Circle { Radius: < 10 }) 14 | { } 15 | else if (item is Circle { Radius: 20 }) 16 | { } 17 | else if (item is Circle ci) 18 | { } -------------------------------------------------------------------------------- /code/pattern-matching-2.kt: -------------------------------------------------------------------------------- 1 | // Not supported yet 2 | // https://youtrack.jetbrains.com/issue/KT-20004 3 | // http://openjdk.java.net/jeps/305 -------------------------------------------------------------------------------- /code/pattern-matching.cs: -------------------------------------------------------------------------------- 1 | // Pre C# 9 2 | var nb = 42; 3 | var text = nb switch 4 | { 5 | int i when i < 10 => "single digit", 6 | 10 => "double digits", 7 | int i when i < 100 => "double digits", 8 | int i when i < 1000 => "triple digits", 9 | _ => "four or more digits" 10 | }; 11 | 12 | // With C# 9 relational and conjunctive patterns 13 | var nb = 42; 14 | var text = nb switch 15 | { 16 | < 10 => "single digit", 17 | 10 or (>= 11 and < 100) => "double digits", 18 | < 1000 => "triple digits", 19 | _ => "for or more digits", 20 | }; 21 | // double digits -------------------------------------------------------------------------------- /code/pattern-matching.kt: -------------------------------------------------------------------------------- 1 | val nb = 42 2 | val text = when (nb) { 3 | in 0..7, 8, 9 -> "single digit" 4 | 10 -> "double digits" 5 | in 11..99 -> "double digits" 6 | in 100..999 -> "triple digits" 7 | else -> "four or more digits" 8 | } 9 | // double digits -------------------------------------------------------------------------------- /code/range-and-index.cs: -------------------------------------------------------------------------------- 1 | var names = new[] { "Anna", "Alex", "Brian", "Jill", "Jack" }; 2 | 3 | foreach (var name in names[1..^1]) 4 | { 5 | Console.WriteLine($"Person is called {name}"); 6 | } 7 | // Person is called Alex 8 | // Person is called Brian 9 | // Person is called Jill 10 | -------------------------------------------------------------------------------- /code/range-and-index.kt: -------------------------------------------------------------------------------- 1 | val names = arrayOf("Anna", "Alex", "Brian", "Jill", "Jack") 2 | val count = names.count() 3 | 4 | for (name in names.slice(1.. { 1, 2, 3, 4 } 3 | .Where(i => 4 | { 5 | Console.WriteLine($"Filter {i}, "); 6 | return i % 2 == 1; 7 | }).Select(i => 8 | { 9 | Console.WriteLine($"Map {i}, "); 10 | return i * 2; 11 | }); 12 | 13 | 14 | var items = query.ToList(); 15 | 16 | //Filter 1, 17 | //Map 1, 18 | //Filter 2, 19 | //Filter 3, 20 | //Map 3, 21 | //Filter 4, -------------------------------------------------------------------------------- /code/sequence.kt: -------------------------------------------------------------------------------- 1 | // Sequence is lazy 2 | val seq = sequenceOf(1, 2, 3, 4) 3 | .filter { println("Filter $it, "); it % 2 == 1 } 4 | .map { println("Map $it, "); it * 2 } 5 | 6 | // Computations are evaluated during terminal operation 7 | val items = seq.toList() 8 | 9 | // Filter 1, 10 | // Map 1, 11 | // Filter 2, 12 | // Filter 3, 13 | // Map 3, 14 | // Filter 4, 15 | 16 | // List is not lazy, so functions are evaluated immediately 17 | val items2 = listOf(1, 2, 3, 4) 18 | .filter { println("Filter $it, "); it % 2 == 1 } 19 | .map { println("Map $it, "); it * 2 } 20 | 21 | // Filter 1, 22 | // Filter 2, 23 | // Filter 3, 24 | // Filter 4, 25 | // Map 1, 26 | // Map 3, -------------------------------------------------------------------------------- /code/single-expression.cs: -------------------------------------------------------------------------------- 1 | // Single expression functions can be without braces 2 | int Double(int value) => value * 2; 3 | 4 | var result = Double(4); 5 | // 8 -------------------------------------------------------------------------------- /code/single-expression.kt: -------------------------------------------------------------------------------- 1 | // Single expression functions can be without braces and return type 2 | fun double(value: Int) = value * 2 3 | 4 | val result = double(4) 5 | // 8 -------------------------------------------------------------------------------- /code/smart-casting.cs: -------------------------------------------------------------------------------- 1 | foreach (var current in library) 2 | { 3 | if (current is Movie movie) 4 | { 5 | Console.WriteLine($"Movie: '{movie.Name}', " + 6 | $"dir: {movie.Director}"); 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /code/smart-casting.kt: -------------------------------------------------------------------------------- 1 | for (current in library) { 2 | if (current is Movie) { 3 | println("Movie: '${current.name}', " + 4 | "dir: ${current.director}") 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /code/sort.cs: -------------------------------------------------------------------------------- 1 | var ordered = new[] { 1, 5, 3, 12, 2 }.OrderBy(i => i); 2 | // [ 1, 2, 3, 5, 12 ] -------------------------------------------------------------------------------- /code/sort.kt: -------------------------------------------------------------------------------- 1 | val ordered = listOf(1, 5, 3, 12, 2).sorted() 2 | // [ 1, 2, 3, 5, 12 ] -------------------------------------------------------------------------------- /code/string-interpolation.cs: -------------------------------------------------------------------------------- 1 | var apples = 3; 2 | var oranges = 5; 3 | var fruitSummary = $"I have {apples + oranges} " + 4 | "pieces of fruit."; 5 | -------------------------------------------------------------------------------- /code/string-interpolation.kt: -------------------------------------------------------------------------------- 1 | val apples = 3 2 | val oranges = 5 3 | val fruitSummary = "I have ${apples + oranges} " + 4 | "pieces of fruit." 5 | -------------------------------------------------------------------------------- /code/subclass.cs: -------------------------------------------------------------------------------- 1 | class NamedShape 2 | { 3 | private readonly string _name; 4 | 5 | public NamedShape(string name) => _name = name; 6 | 7 | protected int NumberOfSides { get; set; } 8 | 9 | public virtual string SimpleDescription() => 10 | $"A shape with {NumberOfSides} sides."; 11 | } 12 | 13 | class Square: NamedShape 14 | { 15 | private readonly double _sideLength; 16 | 17 | public Square(double sideLength, string name) : base(name) 18 | { 19 | _sideLength = sideLength; 20 | NumberOfSides = 4; 21 | } 22 | 23 | public double Area() => Math.Pow(_sideLength, 2); 24 | 25 | override public string SimpleDescription() => 26 | $"A square with sides of length {_sideLength}."; 27 | } 28 | 29 | var square = new Square(5.2, "My square"); 30 | var area = square.Area(); 31 | var desc = square.SimpleDescription(); 32 | -------------------------------------------------------------------------------- /code/subclass.kt: -------------------------------------------------------------------------------- 1 | open class NamedShape(val name: String) { 2 | var numberOfSides = 0 3 | 4 | open fun simpleDescription() = 5 | "A shape with $numberOfSides sides." 6 | } 7 | 8 | class Square(var sideLength: Double, name: String) : 9 | NamedShape(name) { 10 | init { 11 | numberOfSides = 4 12 | } 13 | 14 | fun area() = sideLength.pow(2) 15 | 16 | override fun simpleDescription() = 17 | "A square with sides of length $sideLength." 18 | } 19 | 20 | val square = Square(5.2, "My square") 21 | val area = square.area() 22 | val desc = square.simpleDescription() 23 | -------------------------------------------------------------------------------- /code/test-code.csx: -------------------------------------------------------------------------------- 1 | // For sake of simplicity, some examples are missing some classes and functions 2 | 3 | class Movie 4 | { 5 | public int Id { get; set; } 6 | public string Name { get; set; } 7 | public string Director { get; set; } 8 | } 9 | 10 | class Song 11 | { 12 | public int Id { get; set; } 13 | } 14 | 15 | var library = new List { new Movie { Id = 1, Name = "A", Director = "B " }, new Song { Id = 2 } }; 16 | 17 | private class Shape 18 | { } 19 | 20 | private class Circle : Shape 21 | { } 22 | 23 | private class Square : Shape 24 | { } 25 | 26 | private void ConvertTest(Shape shape) 27 | { 28 | var circle = (Circle)shape; 29 | var circle2 = shape as Circle; 30 | } 31 | 32 | public class SensorData 33 | { 34 | public int Id { get; set; } 35 | public string Location { get; set; } 36 | public double Value { get; set; } 37 | } 38 | 39 | double ToFahrenheit(double celsius) => (celsius * 9) / 5 + 32; -------------------------------------------------------------------------------- /code/test-code.kts: -------------------------------------------------------------------------------- 1 | // For sake of simplicity, some examples are missing some classes and functions 2 | 3 | data class SensorData(var id: Int, var location: String, val value: Double) 4 | data class Location(var location: String, val value: Double) 5 | 6 | data class Movie(val id: Int, val name: String, val director: String) 7 | data class Song(val id: Int) 8 | 9 | val library = listOf(Movie(1, "A", "B"), Song(2)) 10 | 11 | open class Shape() {} 12 | class Circle() : Shape() {} 13 | class Square() : Shape() {} 14 | 15 | val shape = Circle() 16 | 17 | fun test(x : Shape) { 18 | val circle: Circle = x as Circle 19 | //val circle: Circle? = x as Circle? 20 | //val circle: Circle? = x as? Circle 21 | println(circle) 22 | } 23 | 24 | //test(Square()) 25 | 26 | fun toFahrenheit(celsius: Double) = (celsius * 9) / 5 + 32 -------------------------------------------------------------------------------- /code/tuple-return.cs: -------------------------------------------------------------------------------- 1 | (double a, double b, double c) GetGasPrices() => (3.59, 3.69, 3.79); 2 | 3 | var result = GetGasPrices(); 4 | 5 | var (a, b, c) = GetGasPrices(); -------------------------------------------------------------------------------- /code/tuple-return.kt: -------------------------------------------------------------------------------- 1 | // Kotlin doesn't have tuples, use data classes 2 | data class GasPrices(val a: Double, val b: Double, val c: Double) 3 | 4 | fun getGasPrices() = GasPrices(3.59, 3.69, 3.79) 5 | 6 | val prices = getGasPrices(); 7 | 8 | val (a, b, c) = getGasPrices(); -------------------------------------------------------------------------------- /code/type-coercion.cs: -------------------------------------------------------------------------------- 1 | var label = "The width is "; 2 | var width = 94; 3 | var widthLabel = label + width; 4 | // The width is 94 -------------------------------------------------------------------------------- /code/type-coercion.kt: -------------------------------------------------------------------------------- 1 | val label = "The width is " 2 | val width = 94 3 | val widthLabel = label + width 4 | // The width is 94 -------------------------------------------------------------------------------- /code/usage.cs: -------------------------------------------------------------------------------- 1 | var shape = new Shape(); 2 | shape.NumberOfSides = 7; 3 | var shapeDescription = shape.SimpleDescription(); 4 | // A shape with 7 sides. -------------------------------------------------------------------------------- /code/usage.kt: -------------------------------------------------------------------------------- 1 | var shape = Shape() 2 | shape.numberOfSides = 7 3 | var shapeDescription = shape.simpleDescription() 4 | // A shape with 7 sides. -------------------------------------------------------------------------------- /code/variable-number-of-arguments.cs: -------------------------------------------------------------------------------- 1 | int SumOf(params int[] numbers) 2 | { 3 | var sum = 0; 4 | foreach (var number in numbers) 5 | sum += number; 6 | return sum; 7 | } 8 | 9 | var sum = SumOf(42, 597, 12); 10 | 11 | // SumOf() can also be written in a shorter way: 12 | int SumOf(params int[] numbers) => numbers.Sum(); -------------------------------------------------------------------------------- /code/variable-number-of-arguments.kt: -------------------------------------------------------------------------------- 1 | fun sumOf(vararg numbers: Int): Int { 2 | var sum = 0 3 | for (number in numbers) { 4 | sum += number 5 | } 6 | return sum 7 | } 8 | 9 | val sum = sumOf(42, 597, 12) 10 | 11 | // sumOf() can also be written in a shorter way: 12 | fun sumOf(vararg numbers: Int) = numbers.sum() 13 | -------------------------------------------------------------------------------- /code/variables-and-constants.cs: -------------------------------------------------------------------------------- 1 | var myVariable = 42; 2 | myVariable = 50; 3 | 4 | // C# doesn't have local runtime constants 5 | -------------------------------------------------------------------------------- /code/variables-and-constants.kt: -------------------------------------------------------------------------------- 1 | var myVariable = 42 2 | myVariable = 50 3 | 4 | val myConstant = 42 5 | -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Helvetica Neue", sans-serif; 3 | margin: 0; 4 | } 5 | 6 | body * { 7 | box-sizing: border-box; 8 | } 9 | 10 | .section { 11 | background-color: #f4f4f4; 12 | padding: 4em 3em; 13 | } 14 | 15 | .section:nth-child(2n) { 16 | background-color: white; 17 | } 18 | 19 | .title { 20 | font-size: 2.7em; 21 | color: #888; 22 | font-weight: 100; 23 | letter-spacing: 0.2em; 24 | text-align: center; 25 | } 26 | 27 | .case {} 28 | 29 | .name { 30 | font-size: 1.8em; 31 | color: #444; 32 | font-weight: 300; 33 | text-align: center; 34 | margin: 60px; 35 | } 36 | 37 | .pair { 38 | display: flex; 39 | display: -webkit-flex; 40 | display: -ms-flexbox; 41 | justify-content: center; 42 | -webkit-justify-content: center; 43 | -ms-box-pack: center; 44 | -ms-box-align: center; 45 | } 46 | 47 | .card { 48 | flex: 1; 49 | -webkit-flex: 1; 50 | -ms-flex: 1; 51 | width: 50%; 52 | max-width: 650px; 53 | margin: 0 10px; 54 | } 55 | 56 | .lang { 57 | font-size: 1.3em; 58 | color: #666; 59 | padding-bottom: 0; 60 | font-weight: 200; 61 | letter-spacing: 0.07em; 62 | } 63 | 64 | .code { 65 | font-size: 1.15em; 66 | background-color: white; 67 | margin: 0.4em 0 0 0; 68 | padding: 0.4em; 69 | line-height: 1.3em; 70 | } 71 | .section:nth-child(2n) .code { 72 | background-color: #f4f4f4; 73 | } 74 | 75 | #fork-me { 76 | position: absolute; 77 | right: 0; 78 | } 79 | 80 | .hljs { 81 | background-color: transparent; 82 | } 83 | 84 | #note { 85 | font-size: 1.5em; 86 | color: #fff; 87 | text-align: center; 88 | padding: 0.6em; 89 | background: #414141; 90 | font-weight: 300; 91 | letter-spacing: 0.05em; 92 | } 93 | 94 | .disclaimer { 95 | font-size: 0.8em; 96 | text-align: center; 97 | padding: 0.6em; 98 | font-weight: 300; 99 | letter-spacing: 0.05em; 100 | } 101 | -------------------------------------------------------------------------------- /index.cirru: -------------------------------------------------------------------------------- 1 | doctype 2 | 3 | html 4 | head 5 | title "Kotlin is like C#" 6 | meta (:charset utf-8) 7 | meta (:name description) 8 | :content "Kotlin vs C#. Compare the syntax of Kotlin and C# through short code examples.") 9 | link (:rel icon) 10 | :href https://kotlinlang.org/assets/images/favicon.ico 11 | :type image/x-icon 12 | link (:rel stylesheet) 13 | :href https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.11.0/styles/github.min.css 14 | style (@insert css/style.css) 15 | script (:src https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.11.0/highlight.min.js) 16 | script (:src https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.11.0/languages/kotlin.min.js) 17 | script (:src https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.11.0/languages/cs.min.js) 18 | script "hljs.initHighlightingOnLoad();" 19 | 20 | body 21 | a (:target _blank) 22 | :href https://github.com/ttu/kotlin-is-like-csharp 23 | img#fork-me (:src https://github.blog/wp-content/uploads/2008/12/forkme_right_red_aa0000.png?resize=149%2C149) 24 | #note 25 | = "Kotlin is like C#" 26 | 27 | .disclaimer "Compare the syntax of Kotlin vs C#. Don't take language likeness comparison too seriously." 28 | .disclaimer "Fixes, improvents and additions are welcome. Open an issue or a pull request." 29 | .section 30 | .title BASICS 31 | .case (.name "Hello World") $ .pair 32 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/hello-world.kt) 33 | .card (.lang C#) $ pre.code $ code (@insert ./code/hello-world.cs) 34 | .case (.name "Variables and Constants") $ .pair 35 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/variables-and-constants.kt) 36 | .card (.lang C#) $ pre.code $ code (@insert ./code/variables-and-constants.cs) 37 | .case (.name "Explicit Types") $ .pair 38 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/explicit-types.kt) 39 | .card (.lang C#) $ pre.code $ code (@insert ./code/explicit-types.cs) 40 | .case (.name "Type Coercion") $ .pair 41 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/type-coercion.kt) 42 | .card (.lang C#) $ pre.code $ code (@insert ./code/type-coercion.cs) 43 | .case (.name "Compile Time Constants") $ .pair 44 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/compiletime-constants.kt) 45 | .card (.lang C#) $ pre.code $ code (@insert ./code/compiletime-constants.cs) 46 | .case (.name "String Interpolation") $ .pair 47 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/string-interpolation.kt) 48 | .card (.lang C#) $ pre.code $ code (@insert ./code/string-interpolation.cs) 49 | .case (.name "If Expression / Statement") $ .pair 50 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/if-statement.kt) 51 | .card (.lang C#) $ pre.code $ code (@insert ./code/if-statement.cs) 52 | .case (.name "Conditionals") $ .pair 53 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/conditional.kt) 54 | .card (.lang C#) $ pre.code $ code (@insert ./code/conditional.cs) 55 | 56 | .section 57 | .title FUNCTIONS 58 | .case (.name "Functions") $ .pair 59 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/functions.kt) 60 | .card (.lang C#) $ pre.code $ code (@insert ./code/functions.cs) 61 | .case (.name "Single Expression Functions") $ .pair 62 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/single-expression.kt) 63 | .card (.lang C#) $ pre.code $ code (@insert ./code/single-expression.cs) 64 | .case (.name "Named Arguments") $ .pair 65 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/named-arguments.kt) 66 | .card (.lang C#) $ pre.code $ code (@insert ./code/named-arguments.cs) 67 | .case (.name "Default Arguments") $ .pair 68 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/default-arguments.kt) 69 | .card (.lang C#) $ pre.code $ code (@insert ./code/default-arguments.cs) 70 | .case (.name "Variable Number of Arguments") $ .pair 71 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/variable-number-of-arguments.kt) 72 | .card (.lang C#) $ pre.code $ code (@insert ./code/variable-number-of-arguments.cs) 73 | .case (.name "Lambdas") $ .pair 74 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/lambda.kt) 75 | .card (.lang C#) $ pre.code $ code (@insert ./code/lambda.cs) 76 | .case (.name "Higher-Order Functions - Return a Function") $ .pair 77 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/hof-return.kt) 78 | .card (.lang C#) $ pre.code $ code (@insert ./code/hof-return.cs) 79 | .case (.name "HOF - Function as Parameter") $ .pair 80 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/hof-parameter.kt) 81 | .card (.lang C#) $ pre.code $ code (@insert ./code/hof-parameter.cs) 82 | .case (.name "Tuple Return") $ .pair 83 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/tuple-return.kt) 84 | .card (.lang C#) $ pre.code $ code (@insert ./code/tuple-return.cs) 85 | 86 | .section 87 | .title COLLECTIONS 88 | .case (.name "Arrays") $ .pair 89 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/arrays.kt) 90 | .card (.lang C#) $ pre.code $ code (@insert ./code/arrays.cs) 91 | .case (.name "Lists") $ .pair 92 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/lists.kt) 93 | .card (.lang C#) $ pre.code $ code (@insert ./code/lists.cs) 94 | .case (.name "Maps / Dictionaries") $ .pair 95 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/maps.kt) 96 | .card (.lang C#) $ pre.code $ code (@insert ./code/maps.cs) 97 | .case (.name "Empty Collections") $ .pair 98 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/empty-collections.kt) 99 | .card (.lang C#) $ pre.code $ code (@insert ./code/empty-collections.cs) 100 | .case (.name "ForEach") $ .pair 101 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/foreach.kt) 102 | .card (.lang C#) $ pre.code $ code (@insert ./code/foreach.cs) 103 | .case (.name "Range Operator") $ .pair 104 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/range-operator.kt) 105 | .card (.lang C#) $ pre.code $ code (@insert ./code/range-operator.cs) 106 | .case (.name "Inclusive Range Operator") $ .pair 107 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/inclusive-range-operator.kt) 108 | .card (.lang C#) $ pre.code $ code (@insert ./code/inclusive-range-operator.cs) 109 | .case (.name "Collection Range and Index") $ .pair 110 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/range-and-index.kt) 111 | .card (.lang C#) $ pre.code $ code (@insert ./code/range-and-index.cs) 112 | 113 | .section 114 | .title "COLLECTION OPERATIONS / LINQ" 115 | .case (.name "Map") $ .pair 116 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/map.kt) 117 | .card (.lang C#) $ pre.code $ code (@insert ./code/map.cs) 118 | .case (.name "Sort") $ .pair 119 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/sort.kt) 120 | .card (.lang C#) $ pre.code $ code (@insert ./code/sort.cs) 121 | .case (.name "Filter / GroupBy / Average") $ .pair 122 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/collections.kt) 123 | .card (.lang C#) $ pre.code $ code (@insert ./code/collections.cs) 124 | .case (.name "Sequences") $ .pair 125 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/sequence.kt) 126 | .card (.lang C#) $ pre.code $ code (@insert ./code/sequence.cs) 127 | 128 | .section 129 | .title CLASSES 130 | .case (.name "Declaration") $ .pair 131 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/class-declaration.kt) 132 | .card (.lang C#) $ pre.code $ code (@insert ./code/class-declaration.cs) 133 | .case (.name "Usage") $ .pair 134 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/usage.kt) 135 | .card (.lang C#) $ pre.code $ code (@insert ./code/usage.cs) 136 | .case (.name "Subclass") $ .pair 137 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/subclass.kt) 138 | .card (.lang C#) $ pre.code $ code (@insert ./code/subclass.cs) 139 | .case (.name "Data Class / Record") $ .pair 140 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/data-class.kt) 141 | .card (.lang C#) $ pre.code $ code (@insert ./code/data-class.cs) 142 | .case (.name "Immutable Data Class / Record") $ .pair 143 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/immutable-data-class.kt) 144 | .card (.lang C#) $ pre.code $ code (@insert ./code/immutable-data-class.cs) 145 | .case (.name "Extensions / Extension Methods") $ .pair 146 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/extension-methods.kt) 147 | .card (.lang C#) $ pre.code $ code (@insert ./code/extension-methods.cs) 148 | .case (.name "Interfaces") $ .pair 149 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/interface.kt) 150 | .card (.lang C#) $ pre.code $ code (@insert ./code/interface.cs) 151 | 152 | .section 153 | .title TYPES 154 | .case (.name "Checking Type") $ .pair 155 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/checking-type.kt) 156 | .card (.lang C#) $ pre.code $ code (@insert ./code/checking-type.cs) 157 | .case (.name "Casting") $ .pair 158 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/casting.kt) 159 | .card (.lang C#) $ pre.code $ code (@insert ./code/casting.cs) 160 | .case (.name "Smart Cast") $ .pair 161 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/smart-casting.kt) 162 | .card (.lang C#) $ pre.code $ code (@insert ./code/smart-casting.cs) 163 | 164 | .section 165 | .title EXCEPTIONS 166 | .case (.name "Exception Handling") $ .pair 167 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/exceptions.kt) 168 | .card (.lang C#) $ pre.code $ code (@insert ./code/exceptions.cs) 169 | .case (.name "Exception Expression") $ .pair 170 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/exception-expression.kt) 171 | .card (.lang C#) $ pre.code $ code (@insert ./code/exception-expression.cs) 172 | 173 | .section 174 | .title "PATTERN MATCHING" 175 | .case (.name "When / Switch Expression") $ .pair 176 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/pattern-matching.kt) 177 | .card (.lang C#) $ pre.code $ code (@insert ./code/pattern-matching.cs) 178 | .case (.name "Is Expression / When Clause / Property Pattern") $ .pair 179 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/pattern-matching-2.kt) 180 | .card (.lang C#) $ pre.code $ code (@insert ./code/pattern-matching-2.cs) 181 | 182 | .section 183 | .title NULL 184 | .case (.name "Nullable Types") $ .pair 185 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/null-reference.kt) 186 | .card (.lang C#) $ pre.code $ code (@insert ./code/null-reference.cs) 187 | .case (.name "Null Conditional") $ .pair 188 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/null-conditional.kt) 189 | .card (.lang C#) $ pre.code $ code (@insert ./code/null-conditional.cs) 190 | .case (.name "Elvis Operator / Null Coalescing") $ .pair 191 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/null-coalescing.kt) 192 | .card (.lang C#) $ pre.code $ code (@insert ./code/null-coalescing.cs) 193 | .case (.name "Generics, Out and Conditional") $ .pair 194 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/generic-conditional.kt) 195 | .card (.lang C#) $ pre.code $ code (@insert ./code/generic-conditional.cs) 196 | 197 | .section 198 | .title "JSON / DYNAMIC" 199 | .case (.name "Dynamic") $ .pair 200 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/dynamic.kt) 201 | .card (.lang C#) $ pre.code $ code (@insert ./code/dynamic.cs) 202 | 203 | .section 204 | .title "COROUTINES / TASKS" 205 | .case (.name "Async / Await") $ .pair 206 | .card (.lang Kotlin) $ pre.code $ code (@insert ./code/coroutines-tasks.kt) 207 | .card (.lang C#) $ pre.code $ code (@insert ./code/coroutines-tasks.cs) 208 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | Kotlin is like C#
Kotlin is like C#
Compare the syntax of Kotlin vs C#. Don't take language likeness comparison too seriously.
Fixes, improvents and additions are welcome. Open an issue or a pull request.
BASICS
Hello World
Kotlin
println("Hello, world!")
102 | 
C#
public void Main() 
103 | {
104 |     Console.WriteLine("Hello, world!");
105 | }
106 | 
107 | // C# 9 supports top-level statements
108 | Console.WriteLine("Hello, world!");
Variables and Constants
Kotlin
var myVariable = 42
109 | myVariable = 50
110 | 
111 | val myConstant = 42
112 | 
C#
var myVariable = 42;
113 | myVariable = 50;
114 | 
115 | // C# doesn't have local runtime constants
116 | 
Explicit Types
Kotlin
val explicitDouble: Double = 70.0
117 | 
C#
double explicitDouble = 70.0;
118 | 
Type Coercion
Kotlin
val label = "The width is "
119 | val width = 94
120 | val widthLabel = label + width
121 | // The width is 94
C#
var label = "The width is ";
122 | var width = 94;
123 | var widthLabel = label + width;
124 | // The width is 94
Compile Time Constants
Kotlin
const val SYSTEM_DEPRECATED: String = "System is deprecated"
C#
const string SYSTEM_DEPRECATED = "System is deprecated";
String Interpolation
Kotlin
val apples = 3
125 | val oranges = 5
126 | val fruitSummary = "I have ${apples + oranges} " +
127 |                    "pieces of fruit."
128 | 
C#
var apples = 3;
129 | var oranges = 5;
130 | var fruitSummary = $"I have {apples + oranges} " +
131 |                    "pieces of fruit.";
132 | 
If Expression / Statement
Kotlin
val age = 42
133 | 
134 | if (age < 10) {
135 |     println("You're too young to watch this movie")
136 | } else if (age < 13) {
137 |     println("You can watch this movie with a parent")
138 | } else {
139 |     println("You can watch this movie")
140 | }
C#
var age = 42;
141 | 
142 | if (age < 10) 
143 | {
144 |     Console.WriteLine("You're too young to watch this movie");
145 | } 
146 | else if (age < 13) 
147 | {
148 |     Console.WriteLine("You can watch this movie with a parent");
149 | } 
150 | else 
151 | {
152 |     Console.WriteLine("You can watch this movie");
153 | }
Conditionals
Kotlin
// if is an expression, so ternary operation not needed
154 | val loaded = true
155 | val status = if (loaded) "Ready" else "Loading..."
156 | // "Ready
C#
var loaded = true;
157 | var status = loaded ? "Ready" : "Loading...";
158 | // "Ready
FUNCTIONS
Functions
Kotlin
fun greet(name: String, day: String): String {
159 |     return "Hello $name, today is $day."
160 | }
161 | 
162 | val text = greet("Bob", "Tuesday")
163 | // Hello Bob, today is Tuesday.
C#
string Greet(string name, string day)
164 | {
165 |     return $"Hello {name}, today is {day}.";
166 | }
167 | 
168 | var text = Greet("Bob", "Tuesday");
169 | // Hello Bob, today is Tuesday
Single Expression Functions
Kotlin
// Single expression functions can be without braces and return type
170 | fun double(value: Int) = value * 2
171 | 
172 | val result = double(4)
173 | // 8
C#
// Single expression functions can be without braces
174 | int Double(int value) => value * 2;
175 | 
176 | var result = Double(4);
177 | // 8
Named Arguments
Kotlin
fun area(width: Int, height: Int) = width * height
178 | var result = area(width = 2, height = 3)
179 | 
180 | // This is also possible with named arguments
181 | result = area(2, height = 2)
182 | result = area(height = 3, width = 2)
183 | 
C#
int Area(int width, int height) => width * height;
184 | var result = Area(width: 2, height: 3);
185 | 
186 | // This is also possible with named arguments
187 | result = Area(2, height: 2);
188 | result = Area(height: 3, width: 2);
Default Arguments
Kotlin
fun displayGreeting(message: String, name: String = "Guest") {
189 |     println("Hello $name, $message")
190 | }
191 | 
192 | displayGreeting("welcome!")
193 | // Hello Guest, welcome!
C#
void DisplayGreeting(string message, string name = "Guest") 
194 | {
195 |     Console.WriteLine($"Hello {name}, {message}");
196 | }
197 | 
198 | DisplayGreeting("welcome!");
199 | // Hello Guest, welcome!
Variable Number of Arguments
Kotlin
fun sumOf(vararg numbers: Int): Int {
200 |     var sum = 0
201 |     for (number in numbers) {
202 |         sum += number
203 |     }
204 |     return sum
205 | }
206 | 
207 | val sum = sumOf(42, 597, 12)
208 | 
209 | // sumOf() can also be written in a shorter way:
210 | fun sumOf(vararg numbers: Int) = numbers.sum()
211 | 
C#
int SumOf(params int[] numbers)
212 | {
213 |     var sum = 0;
214 |     foreach (var number in numbers) 
215 |         sum += number;
216 |     return sum;
217 | }
218 | 
219 | var sum = SumOf(42, 597, 12);
220 | 
221 | // SumOf() can also be written in a shorter way:
222 | int SumOf(params int[] numbers) => numbers.Sum();
Lambdas
Kotlin
fun containsEven(numbers: List<Int>) = numbers.any { it % 2 == 0 }
C#
bool ContainsEven(List<int> numbers) => numbers.Any(e => e % 2 == 0);
Higher-Order Functions - Return a Function
Kotlin
fun makeIncrementer(): (Int) -> Int {
223 |     val addOne = fun(number: Int): Int {
224 |         return 1 + number
225 |     }
226 |     return addOne
227 | }
228 | 
229 | val increment = makeIncrementer()
230 | val result = increment(7)
231 | 
232 | // makeIncrementer can also be written in a shorter way:
233 | fun makeIncrementer() = fun(number: Int) = 1 + number
234 | 
C#
Func<int, int> MakeIncrementer()
235 | {
236 |     int addOne(int number) => 1 + number;
237 |     return addOne;
238 | }
239 | 
240 | var increment = MakeIncrementer();
241 | var result = increment(7);
242 | 
243 | // MakeIncrementer can also be written in a shorter way:
244 | Func<int, int> MakeIncrementer() => i => 1 + i;
245 | 
HOF - Function as Parameter
Kotlin
fun transform(initial: String, f: (String) -> String) = f(initial)
246 | 
247 | val result = transform("hello", { x -> x.toUpperCase() })
248 | // HELLO
249 | 
250 | // Trailing lambda can be placed outside the parentheses
251 | val result2 = transform("hello") { x -> x.toUpperCase() }
C#
string Transform(string initial, Func<string, string> f) => f(initial);
252 | 
253 | var result = Transform("hello", x => x.ToUpper());
254 | // HELLO
Tuple Return
Kotlin
// Kotlin doesn't have tuples, use data classes
255 | data class GasPrices(val a: Double, val b: Double, val c: Double)
256 | 
257 | fun getGasPrices() = GasPrices(3.59, 3.69, 3.79)
258 | 
259 | val prices = getGasPrices();
260 | 
261 | val (a, b, c) = getGasPrices();
C#
(double a, double b, double c) GetGasPrices() => (3.59, 3.69, 3.79);
262 | 
263 | var result = GetGasPrices();
264 | 
265 | var (a, b, c) = GetGasPrices();
COLLECTIONS
Arrays
Kotlin
val shoppingList = arrayOf("catfish", "water",
266 |     "tulips", "blue paint")
267 | shoppingList[1] = "bottle of water"
268 | 
C#
var shoppingList = new[] { "catfish", "water",
269 |     "tulips", "blue paint" };
270 | shoppingList[1] = "bottle of water";
Lists
Kotlin
val shoppingList = listOf("catfish", "water",
271 |     "tulips", "blue paint")
272 | 
273 | val shoppingListMutable = mutableListOf("catfish", "water",
274 |     "tulips", "blue paint")
275 | shoppingListMutable[1] = "bottle of water"
276 | shoppingListMutable.add("bucket")
277 | 
C#
IReadOnlyList<string> shoppingList = new List<string> { "catfish", 
278 |                     "water", "tulips", "blue paint" };
279 | 
280 | var shoppingListMutable = new List<string> { "catfish", "water",
281 |         "tulips", "blue paint" };
282 | shoppingListMutable[1] = "bottle of water";
283 | shoppingListMutable.Add("bucket");
Maps / Dictionaries
Kotlin
val occupations = mapOf(
284 |     "Malcolm" to "Captain",
285 |     "Kaylee" to "Mechanic"
286 | )
287 | 
288 | val occupationsMutable = mutableMapOf(
289 |     "Malcolm" to "Captain",
290 |     "Kaylee" to "Mechanic"
291 | )
292 | occupationsMutable["Jayne"] = "Public Relations"
293 | occupationsMutable.put("Rick", "Navigation")
294 | 
C#
IReadOnlyDictionary<string,string> occupations = 
295 |                         new Dictionary<string, string>
296 | {
297 |     ["Malcolm"] = "Captain",
298 |     ["Kaylee"] = "Mechanic"
299 | };
300 | 
301 | var occupationsMutable = new Dictionary<string, string>
302 | {
303 |     ["Malcolm"] = "Captain",
304 |     ["Kaylee"] = "Mechanic"
305 | };
306 | occupationsMutable["Jayne"] = "Public Relations";
307 | occupationsMutable.Add("Rick", "Navigation");
308 | 
Empty Collections
Kotlin
val emptyList = mutableListOf<String>()
309 | val emptyMap = mutableMapOf<String, Float>()
310 | 
311 | // read-only empty list
312 | val empty = emptyList<String>()
C#
var emptyList = new List<string>();
313 | var emptyDictionary = new Dictionary<string, float>();
314 | 
315 | // read-only empty list
316 | var empty = Enumerable.Empty<string>();
ForEach
Kotlin
val names = arrayOf("Anna", "Alex", "Brian", "Jack")
317 | 
318 | for (name in names) {
319 |     println("Person is called $name")
320 | }
321 | 
322 | names.forEach { println("Person is called $it") }
323 | 
324 | // Person is called Anna
325 | // Person is called Alex
326 | // Person is called Brian
327 | // Person is called Jack
C#
var names = new List<string> { "Anna", "Alex", "Brian", "Jack" };
328 | 
329 | foreach (var name in names)
330 | {
331 |     Console.WriteLine($"Person is called {name}");
332 | }
333 | 
334 | names.ForEach(name => Console.WriteLine($"Person is called {name}"));
335 | 
336 | // Person is called Anna
337 | // Person is called Alex
338 | // Person is called Brian
339 | // Person is called Jack
Range Operator
Kotlin
val names = arrayOf("Anna", "Alex", "Brian", "Jack")
340 | val count = names.count()
341 | 
342 | for (i in 0..
C#
var names = new[] { "Anna", "Alex", "Brian", "Jack" };
350 | var count = names.Count();
351 | 
352 | foreach (var i in Enumerable.Range(0, count))
353 | {
354 |     Console.WriteLine($"Person {i + 1} is called {names[i]}");
355 | }
356 | // Person 1 is called Anna
357 | // Person 2 is called Alex
358 | // Person 3 is called Brian
359 | // Person 4 is called Jack
360 | 
Inclusive Range Operator
Kotlin
for (index in 1..5) {
361 |     println("$index times 5 is ${index * 5}")
362 | }
363 | // 1 times 5 is 5
364 | // 2 times 5 is 10
365 | // 3 times 5 is 15
366 | // 4 times 5 is 20
367 | // 5 times 5 is 25
368 | 
C#
foreach (var index in Enumerable.Range(1, 5))
369 | {
370 |     Console.WriteLine($"{index} times 5 is {index * 5}");
371 | }
372 | // 1 times 5 is 5
373 | // 2 times 5 is 10
374 | // 3 times 5 is 15
375 | // 4 times 5 is 20
376 | // 5 times 5 is 25
377 | 
Collection Range and Index
Kotlin
val names = arrayOf("Anna", "Alex", "Brian", "Jill", "Jack")
378 | val count = names.count()
379 | 
380 | for (name in names.slice(1..
C#
var names = new[] { "Anna", "Alex", "Brian", "Jill", "Jack" };
386 | 
387 | foreach (var name in names[1..^1])
388 | {
389 |     Console.WriteLine($"Person is called {name}");
390 | }
391 | // Person is called Alex
392 | // Person is called Brian
393 | // Person is called Jill
394 | 
COLLECTION OPERATIONS / LINQ
Map
Kotlin
val numbers = listOf(20, 19, 7, 12)
395 | val multiplied = numbers.map { 3 * it }
396 | // [ 60, 57, 21, 36 ]
397 | 
C#
var numbers = new[] { 20, 19, 7, 12 };
398 | var multiplied = numbers.Select(e => 3 * e);
399 | // [ 60, 57, 21, 36 ]
Sort
Kotlin
val ordered = listOf(1, 5, 3, 12, 2).sorted()
400 | // [ 1, 2, 3, 5, 12 ]
C#
var ordered = new[] { 1, 5, 3, 12, 2 }.OrderBy(i => i);
401 | // [ 1, 2, 3, 5, 12 ]
Filter / GroupBy / Average
Kotlin
val datas = listOf(
402 |     SensorData(1, "A", 2.89),
403 |     SensorData(2, "B", 12.01),
404 |     SensorData(3, "B", 11.89),
405 |     SensorData(4, "A", 3.11),
406 |     SensorData(5, "A", -456.0)
407 | )
408 | 
409 | val avgs = datas
410 |     .filter { it.value > -50.0 }
411 |     .groupBy(SensorData::location)
412 |     .map { Location(it.key, it.value.map(SensorData::value).average()) }
413 | 
414 | // (location=A, value=3.0)
415 | // (location=B, value=11.95)
C#
var datas = new List<SensorData> 
416 | {
417 |     new SensorData { Id = 1, Location = "A", Value = 2.89 },
418 |     new SensorData { Id = 2, Location = "B", Value = 12.01 },
419 |     new SensorData { Id = 3, Location = "B", Value = 11.89 },
420 |     new SensorData { Id = 4, Location = "A", Value = 3.11 },
421 |     new SensorData { Id = 5, Location = "A", Value = -456.0 }
422 | };
423 | 
424 | var avgs = datas
425 |             .Where(e => e.Value > -50.0)
426 |             .GroupBy(e => e.Location)
427 |             .Select(g => new { 
428 |                     Location = g.Key,
429 |                     Value = g.Average(e => e.Value) });
430 | 
431 | // { Location = A, Value = 3.0 }
432 | // { Location = B, Value = 11.95 }
Sequences
Kotlin
// Sequence is lazy
433 | val seq = sequenceOf(1, 2, 3, 4)
434 |     .filter { println("Filter $it, "); it % 2 == 1 }
435 |     .map { println("Map $it, "); it * 2 }
436 | 
437 | // Computations are evaluated during terminal operation
438 | val items = seq.toList()
439 | 
440 | // Filter 1,
441 | // Map 1,
442 | // Filter 2,
443 | // Filter 3,
444 | // Map 3,
445 | // Filter 4,
446 | 
447 | // List is not lazy, so functions are evaluated immediately
448 | val items2 = listOf(1, 2, 3, 4)
449 |     .filter { println("Filter $it, "); it % 2 == 1 }
450 |     .map { println("Map $it, "); it * 2 }
451 | 
452 | // Filter 1,
453 | // Filter 2,
454 | // Filter 3,
455 | // Filter 4,
456 | // Map 1,
457 | // Map 3,
C#
// LINQ is lazy, so no need to use other collection types
458 | var query = new List { 1, 2, 3, 4 }
459 |         .Where(i =>
460 |         {
461 |             Console.WriteLine($"Filter {i}, ");
462 |             return i % 2 == 1;
463 |         }).Select(i =>
464 |         {
465 |             Console.WriteLine($"Map {i}, ");
466 |             return i * 2;
467 |         });
468 | 
469 | 
470 | var items = query.ToList();
471 | 
472 | //Filter 1,
473 | //Map 1,
474 | //Filter 2,
475 | //Filter 3,
476 | //Map 3,
477 | //Filter 4,
CLASSES
Declaration
Kotlin
class Shape {
478 |     var numberOfSides = 0
479 |     fun simpleDescription() =
480 |         "A shape with $numberOfSides sides."
481 | }
482 | 
C#
class Shape 
483 | {
484 |     public int NumberOfSides { get; set; }
485 |     public string SimpleDescription() => 
486 |         $"A shape with {NumberOfSides} sides.";
487 | }
488 | 
Usage
Kotlin
var shape = Shape()
489 | shape.numberOfSides = 7
490 | var shapeDescription = shape.simpleDescription()
491 | // A shape with 7 sides.
C#
var shape = new Shape();
492 | shape.NumberOfSides = 7;
493 | var shapeDescription = shape.SimpleDescription();
494 | // A shape with 7 sides.
Subclass
Kotlin
open class NamedShape(val name: String) {
495 |     var numberOfSides = 0
496 | 
497 |     open fun simpleDescription() =
498 |         "A shape with $numberOfSides sides."
499 | }
500 | 
501 | class Square(var sideLength: Double, name: String) :
502 |         NamedShape(name) {
503 |     init {
504 |         numberOfSides = 4
505 |     }
506 | 
507 |     fun area() = sideLength.pow(2)
508 | 
509 |     override fun simpleDescription() =
510 |         "A square with sides of length $sideLength."
511 | }
512 | 
513 | val square = Square(5.2, "My square")
514 | val area = square.area()
515 | val desc = square.simpleDescription()
516 | 
C#
class NamedShape
517 | {
518 |     private readonly string _name;
519 | 
520 |     public NamedShape(string name) => _name = name;
521 | 
522 |     protected int NumberOfSides { get; set; }
523 | 
524 |     public virtual string SimpleDescription() =>
525 |          $"A shape with {NumberOfSides} sides.";
526 | }
527 | 
528 | class Square: NamedShape
529 | {
530 |     private readonly double _sideLength;
531 | 
532 |     public Square(double sideLength, string name) : base(name)
533 |     {
534 |         _sideLength = sideLength;
535 |         NumberOfSides = 4;
536 |     }
537 | 
538 |     public double Area() => Math.Pow(_sideLength, 2);
539 | 
540 |     override public string SimpleDescription() =>
541 |         $"A square with sides of length {_sideLength}.";
542 | }
543 | 
544 | var square = new Square(5.2, "My square");
545 | var area = square.Area();
546 | var desc = square.SimpleDescription();
547 | 
Data Class / Record
Kotlin
data class Customer(var id: Long, var name: String)
548 | 
549 | val customer = Customer(1, "Sachin")
550 | 
551 | val name = customer.name
552 | customer.id = 2
553 | 
C#
// Pre C# 9 doesn't have data classes
554 | public class Customer
555 | {
556 |     public int Id { get; set; }
557 |     public string Name { get; set; }
558 | }
559 | 
560 | var customer = new Customer { Id  = 1, Name = "Sachin" };
561 | 
562 | var name = customer.Name;
563 | customer.Id = 2
564 | 
565 | // C# 9 has records
566 | // Records can be mutable, but they are primarily 
567 | // intended for supporting immutable data models
568 | public record Customer
569 | {
570 |     public int Id { get; set; }
571 |     public string Name { get; set; }
572 | }
Immutable Data Class / Record
Kotlin
data class Customer(val id: Long, val name: String)
573 | 
574 | val customer = Customer(1, "Sachin")
575 | 
576 | val name = customer.name
577 | customer.id = 2 // Error
578 | 
C#
// C# 9 immutable records can be created with positional parameters
579 | public record Customer(int Id, string Name);
580 | 
581 | Customer customer = new(1, "Sachin");
582 | customer.Id = 2 // Error
583 | 
584 | // or with standard property syntax and init only setter
585 | public record Customer
586 | {
587 |     public int Id { get; init; }
588 |     public string Name { get; init; }
589 | };
590 | 
591 | var customer = new Customer { Id  = 1, Name = "Sachin" };
592 | customer.Id = 2 // Error
593 | 
594 | // Pre C# 9
595 | public class Customer
596 | {
597 |     public Customer(int id, string name) => (Id, Name) = (id, name);
598 |     
599 |     public int Id { get; }
600 |     public string Name { get; }
601 | }
602 | 
603 | var customer = new Customer(1, "Sachin");
604 | 
605 | var name = customer.Name;
606 | customer.Id = 2 // Error
Extensions / Extension Methods
Kotlin
fun MutableList<Int>.swap(idx1: Int, idx2: Int) {
607 |     val tmp = this[idx1]
608 |     this[idx1] = this[idx2]
609 |     this[idx2] = tmp
610 | }
611 | 
612 | val list = mutableListOf(1, 5, 3)
613 | list.swap(0, 2)
614 | // [ 3, 5, 1 ]
C#
public static class Extensions 
615 | {
616 |     public static void Swap(this List<int> list, int idx1, int idx2)
617 |     {
618 |         var temp = list[idx1];
619 |         list[idx1] = list[idx2];
620 |         list[idx2] = temp;
621 |     }
622 | }
623 | 
624 | var list = new List<int> { 1, 5, 3 };
625 | list.Swap(0, 2);
626 | // [ 3, 5, 1 ]
627 | 
Interfaces
Kotlin
interface Nameable {
628 |     fun name(): String
629 | }
630 | 
631 | fun <T: Nameable> genericFunction(x: T) {
632 |     println("Name is " + x.name())
633 | }
634 | 
635 | class Person : Nameable {
636 |     override fun name() = "Person A"
637 | }
638 | 
C#
interface INameable 
639 | {
640 |     string Name();
641 | }
642 | 
643 | void GenericMethod<T>(T x) where T : INameable
644 | {
645 |     Console.WriteLine("Name is " + x.Name());
646 | }
647 | 
648 | class Person : INameable
649 | {
650 |     public string Name() => "Person A";
651 | }
TYPES
Checking Type
Kotlin
var movieCount = 0
652 | var songCount = 0
653 | 
654 | for (item in library) {
655 |     if (item is Movie) {
656 |         ++movieCount
657 |     } else if (item is Song) {
658 |         ++songCount
659 |     }
660 | }
661 | 
C#
var movieCount = 0;
662 | var songCount = 0;
663 | 
664 | foreach (var item in library) 
665 | {
666 |     if (item is Movie)
667 |         ++movieCount;
668 |     else if (item is Song)
669 |         ++songCount;
670 | }
671 | 
Casting
Kotlin
// Unsafe (throw exception)
672 | val circle: Circle = shape as Circle
673 | 
674 | // Safe (return null)
675 | val circle: Circle? = shape as Circle?
676 | 
677 | val circle: Circle? = shape as? Circle
C#
// Unsafe (throw exception)
678 | var circle = (Circle)shape;
679 | 
680 | // Safe (return null)
681 | var circle = shape as Circle;
682 | 
683 | // If Nullable reference types enabled (optional feature)
684 | Circle? circle = shape as Circle;
Smart Cast
Kotlin
for (current in library) {
685 |     if (current is Movie) {
686 |         println("Movie: '${current.name}', " +
687 | 	    "dir: ${current.director}")
688 |     }
689 | }
690 | 
C#
foreach (var current in library)
691 | {
692 |     if (current is Movie movie)
693 |     {
694 |         Console.WriteLine($"Movie: '{movie.Name}', " +
695 | 	    $"dir: {movie.Director}");
696 |     }
697 | }
698 | 
EXCEPTIONS
Exception Handling
Kotlin
try {
699 |     // some code
700 | }
701 | catch (e: SomeException) {
702 |     if (e.SomeCode == 404) {
703 |         // handle SomeException when SomeCode is 404
704 |     } else {
705 |         // handle SomeException
706 |     }
707 | }
708 | catch (e: Exception) {
709 |     // handle rest of the Exceptions
710 | }
711 | finally {
712 |     // optional finally block
713 | }
C#
try 
714 | {
715 |     // Some code
716 | }
717 | catch (SomeException e) when (e.SomeCode == 404) 
718 | {
719 |     // Handle SomeException only when SomeCode is 404
720 | }
721 | catch (SomeException e) 
722 | {
723 |     // Handle SomeException
724 | }
725 | catch (Exception e) 
726 | {
727 |     // Handle rest of the Exceptions 
728 | }
729 | finally 
730 | {
731 |     // Optional finally block
732 | }
Exception Expression
Kotlin
// try is an expression, i.e., it may have a return value
733 | val a: Int? = try { input.toInt() } 
734 |               catch (e: NumberFormatException) { null }
C#
// try is not an expression
735 | int? a;
736 | try { a = int.Parse(input); }
737 | catch { a = null; }
PATTERN MATCHING
When / Switch Expression
Kotlin
val nb = 42
738 | val text = when (nb) {
739 |     in 0..7, 8, 9 -> "single digit"
740 |     10 -> "double digits"
741 |     in 11..99 -> "double digits"
742 |     in 100..999 -> "triple digits"
743 |     else -> "four or more digits"
744 | }
745 | // double digits
C#
// Pre C# 9
746 | var nb = 42;
747 | var text = nb switch
748 | {
749 |     int i when i < 10 => "single digit",
750 |     10 => "double digits",
751 |     int i when i < 100 => "double digits",
752 |     int i when i < 1000 => "triple digits",
753 |     _ => "four or more digits"
754 | };
755 | 
756 | // With C# 9 relational and conjunctive patterns
757 | var nb = 42;
758 | var text = nb switch
759 | {
760 |     < 10 => "single digit",
761 |     10 or (>= 11 and < 100) => "double digits",
762 |     < 1000 => "triple digits",
763 |     _ => "for or more digits",
764 | };
765 | // double digits
Is Expression / When Clause / Property Pattern
Kotlin
// Not supported yet
766 | // https://youtrack.jetbrains.com/issue/KT-20004
767 | // http://openjdk.java.net/jeps/305
C#
var result = item switch
768 | {
769 |     Square s => Handle(s),
770 |     Circle { Radius: < 10 } c => HandleUnder10(c),
771 |     Circle { Radius: 20 } c => Handle20(c),
772 |     Circle c => Handle(c),
773 |     _ => throw new Exception("Unknown shape")
774 | };
775 | 
776 | // Same with if statements
777 | if (item is Square s)
778 | { }
779 | else if (item is Circle { Radius: < 10 })
780 | { }
781 | else if (item is Circle { Radius: 20 })
782 | { }
783 | else if (item is Circle ci)
784 | { }
NULL
Nullable Types
Kotlin
data class Measurement(val celsius: Double)
785 | 
786 | val data: Measurement = null // Error: can't be null
787 | val data: Measurement? = null // Ok: can be null
788 | 
789 | fun printMayBeNull(data: Measurement?) {
790 |     // data can be null, must have null check
791 |     if (data == null)
792 |         return
793 | 
794 |     println(data.celsius)
795 | }
796 | 
797 | fun printNoNull(data: Measurement) {
798 |     // data can't be null. No need for check
799 |     println(data.celsius)
800 | }
801 | 
802 | val current: Measurement? = getDataFromApi()
803 | 
804 | printMayBeNull(current) // Ok: can be null
805 | 
806 | if (current == null)
807 |     return
808 | 
809 | printNoNull(current)
810 | 
C#
// Nullable reference types are optional feature in C#
811 | 
812 | public class Measurement 
813 | { 
814 |     public double Celsius { get; set; }
815 | }
816 | 
817 | Measurement? data = null; // Ok: can be null
818 | Measurement data = null; // Error: can't be null
819 | 
820 | void PrintMayBeNull(Measurement? data) 
821 | {
822 |     // data can be null, must have null check
823 |     if (data == null)
824 |         return;
825 | 
826 |     Console.WriteLine(data.Celsius);
827 | }
828 | 
829 | void PrintNoNull(Measurement data) 
830 | {
831 |     // data can't be null. No need for check
832 |     Console.WriteLine(data.Celsius);
833 | }
834 | 
835 | Measurement? current = GetDataFromApi();
836 | 
837 | PrintMayBeNull(current); // No need for check as method accepts nulls
838 | 
839 | if (current == null)
840 |     return; 
841 | 
842 | PrintNoNull(current); // OK: Null is checked before method call
Null Conditional
Kotlin
data class DataPoint(val id: Int, val celsius: Double, 
843 |     val child: DataPoint? = null)
844 | 
845 | val data = DataPoint(1, 22.1, DataPoint(2, 22.8))
846 | 
847 | val result = data.child?.child?.
848 |             let { toFahrenheit(it.celsius) } ?: Double.MIN_VALUE
C#
public class DataPoint
849 | {
850 |     public int Id { get; set; }
851 |     public double Celsius { get; set; }
852 |     public DataPoint? Child { get; set; }
853 | }
854 |         
855 | var data = new DataPoint 
856 | { 
857 |     Id = 1, 
858 |     Celsius = 22.1, 
859 |     Child = new DataPoint { Id = 2, Celsius = 22.8 }
860 | };
861 | 
862 | var result = data.Child?.Child != null
863 |                 ? ToFahrenheit(data.Child.Child.Celsius) 
864 |                 : double.MinValue;
Elvis Operator / Null Coalescing
Kotlin
val data = DataPoint(1, 22.1, DataPoint(2, 22.8))
865 | 
866 | val result = data.child?.child?.celsius ?: Double.MIN_VALUE
867 | // Double.MIN_VALUE
C#
var data = new DataPoint 
868 | { 
869 |     Id = 1, 
870 |     Celsius = 22.1, 
871 |     Child = new DataPoint { Id = 2, Celsius = 22.8 }
872 | };
873 | 
874 | var result = data.Child?.Child?.Celsius ?? double.MinValue;
875 | // double.MinValue
Generics, Out and Conditional
Kotlin
// Use .let and forget weird helper methods
876 | 
877 | val data = DataPoint(1, 22.1, DataPoint(2, 22.8))
878 | 
879 | val result = data.child?.child?.
880 |             let { toFahrenheit(it.celsius) } ?: Double.MIN_VALUE
C#
// Generic helper method that will return boolean and set output
881 | bool GetValue<T>(T input, out T output)
882 | {
883 |     output = input;
884 |     return output != null;
885 | }
886 | 
887 | var data = new DataPoint 
888 | { 
889 |     Id = 1, 
890 |     Celsius = 22.1, 
891 |     Child = new DataPoint { Id = 2, Celsius = 22.8 }
892 | };
893 | 
894 | var result = GetValue(data.Child?.Child, out var output)
895 |                 ? ToFahrenheit(output.Celsius) 
896 |                 : double.MinValue;
897 | 
898 | string set = "My text";
899 | var text = GetValue(set, out var output) ? output : "Not set";
900 | // "My text"
901 | string notSet = null;
902 | var text = GetValue(notSet, out var output) ? output : "Not set";
903 | // "Not set"
904 | 
JSON / DYNAMIC
Dynamic
Kotlin
// The dynamic type is not supported in code targeting the JVM
905 | // https://kotlinlang.org/docs/reference/dynamic-type.html
906 | 
907 | // JSON example with data classes
908 | data class Work(val name: String, val location: String)
909 | data class User(val id: String, val work: Work)
910 | 
911 | val json = """[
912 |     { "id": "A", "work": { "name": "ACME 2", "location": "NY" } },
913 |     { "id": "B", "work": { "name": "Box Co", "location": "SF" } },
914 |     { "id": "C", "work": { "name": "DotCom", "location": "NY" } }
915 | ]"""
916 | 
917 | val users =  jacksonObjectMapper().readValue<List<User>>(json)
918 | 
919 | val name = users.first().work.name
920 | // ACME 2
921 | 
922 | val fromNy = users
923 |               .filter { it.work.location == "NY" }
924 |               .map { it.id }
925 | // [A, C]
C#
/* with dynamic, type is not known until runtime */
926 | 
927 | var json = @"[
928 |   { 'id': 'A', 'work': { 'name': 'ACME 2', 'location': 'NY' } },
929 |   { 'id': 'B', 'work': { 'name': 'Box Co', 'location': 'SF' } },
930 |   { 'id': 'C', 'work': { 'name': 'DotCom', 'location': 'NY' } }
931 | ]";
932 | 
933 | var users = JsonConvert.DeserializeObject<List<dynamic>>(json);
934 | 
935 | var name = users.First().work.name;
936 | // ACME 2
937 | 
938 | var fromNY = users
939 |               .Where(e => e.work.location == "NY")
940 |               .Select(e => e.id);
941 | // [A, C]
COROUTINES / TASKS
Async / Await
Kotlin
data class Stats(val full_name: String, val stargazers_count: Int = -1, val forks: Int = -1)
942 | 
943 | val mapper = jacksonObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false)
944 | 
945 | val repos = listOf("jetbrains/kotlin", "dotnet/csharplang")
946 | 
947 | val asyncRequests = repos.map { repo ->
948 |     GlobalScope.async {
949 |         val body = Fuel.get("https://api.github.com/repos/$repo")
950 |             .responseString()
951 |             .third.component1() // Fuel Result & Body
952 |         body?.let { mapper.readValue(it) } ?: Stats(repo)
953 |     }
954 | }
955 | 
956 | runBlocking {
957 |     val results = asyncRequests.map { it.await() }
958 |     results.forEach{ println("${it.full_name} : ${it.stargazers_count} - ${it.forks}") }
959 | }
C#
var client = new HttpClient();
960 | 
961 | var repos = new [] { "jetbrains/kotlin", "dotnet/csharplang" };
962 | 
963 | var asyncRequests = repos.Select(async repo =>
964 | {
965 |     var response = await client.GetAsync($"https://api.github.com/repos/{repo}");
966 |     var json = await response.Content.ReadAsStringAsync();
967 |     dynamic content = JsonConvert.DeserializeObject(json);
968 |     return new { repo, stars = content.stargazers_count, forks = content.forks };
969 | });
970 | 
971 | var results = await Task.WhenAll(asyncRequests);
972 | 
973 | foreach(var data in results)
974 |     Console.WriteLine($"{data.repo} : {data.stars} - {data.forks}");
-------------------------------------------------------------------------------- /make.coffee: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env coffee 2 | project = 'kotlin-is-like-csharp' 3 | 4 | require 'shelljs/make' 5 | path = require 'path' 6 | mission = require 'mission' 7 | 8 | mission.time() 9 | 10 | cirru = (data) -> 11 | mission.cirruHtml 12 | file: 'index.cirru' 13 | from: './' 14 | to: './' 15 | extname: '.html' 16 | data: data 17 | 18 | target.dev = -> 19 | cirru inDev: yes 20 | 21 | target.watch = -> 22 | station = mission.reload() 23 | 24 | mission.watch 25 | files: ['cirru/', 'code/'] 26 | trigger: (filepath, extname) -> 27 | cirru inDev: yes 28 | station.reload project 29 | 30 | target.patch = -> 31 | mission.bump 32 | file: 'package.json' 33 | options: 34 | at: 'patch' 35 | -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kotlin-is-like-csharp", 3 | "version": "0.0.1", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "amdefine": { 8 | "version": "1.0.1", 9 | "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", 10 | "integrity": "sha1-SlKCrBZHKek2Gbz9OtFR+BfOkfU=", 11 | "dev": true 12 | }, 13 | "asn1": { 14 | "version": "0.1.11", 15 | "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.1.11.tgz", 16 | "integrity": "sha1-VZvhg3bQik7E2+gId9J4GGObLfc=", 17 | "dev": true, 18 | "optional": true 19 | }, 20 | "assert-plus": { 21 | "version": "0.1.5", 22 | "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-0.1.5.tgz", 23 | "integrity": "sha1-7nQAlBMALYTOxyGcasgRgS5yMWA=", 24 | "dev": true, 25 | "optional": true 26 | }, 27 | "async": { 28 | "version": "0.2.10", 29 | "resolved": "https://registry.npmjs.org/async/-/async-0.2.10.tgz", 30 | "integrity": "sha1-trvgsGdLnXGXCMo43owjfLUmw9E=", 31 | "dev": true 32 | }, 33 | "aws-sign2": { 34 | "version": "0.5.0", 35 | "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.5.0.tgz", 36 | "integrity": "sha1-xXED96F/wDfwLXwuZLYC6iI/fWM=", 37 | "dev": true, 38 | "optional": true 39 | }, 40 | "balanced-match": { 41 | "version": "1.0.0", 42 | "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", 43 | "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", 44 | "dev": true 45 | }, 46 | "boom": { 47 | "version": "0.4.2", 48 | "resolved": "https://registry.npmjs.org/boom/-/boom-0.4.2.tgz", 49 | "integrity": "sha1-emNune1O/O+xnO9JR6PGffrukRs=", 50 | "dev": true, 51 | "optional": true, 52 | "requires": { 53 | "hoek": "0.9.x" 54 | } 55 | }, 56 | "brace-expansion": { 57 | "version": "1.1.11", 58 | "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", 59 | "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", 60 | "dev": true, 61 | "requires": { 62 | "balanced-match": "^1.0.0", 63 | "concat-map": "0.0.1" 64 | } 65 | }, 66 | "camelcase": { 67 | "version": "1.2.1", 68 | "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-1.2.1.tgz", 69 | "integrity": "sha1-m7UwTS4LVmmLLHWLCKPqqdqlijk=", 70 | "dev": true 71 | }, 72 | "character-parser": { 73 | "version": "1.2.0", 74 | "resolved": "https://registry.npmjs.org/character-parser/-/character-parser-1.2.0.tgz", 75 | "integrity": "sha1-lBNNbl2HCjm+NZ99IkYJNRhN3vY=", 76 | "dev": true 77 | }, 78 | "chokidar": { 79 | "version": "0.8.4", 80 | "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-0.8.4.tgz", 81 | "integrity": "sha1-OytQZoFwhlNLqBoJK9z0viW4vuA=", 82 | "dev": true, 83 | "requires": { 84 | "fsevents": "github:pipobscure/fsevents#7dcdf9fa3f8956610fd6f69f72c67bace2de7138", 85 | "recursive-readdir": "0.0.2" 86 | } 87 | }, 88 | "cirru-html": { 89 | "version": "0.2.2", 90 | "resolved": "https://registry.npmjs.org/cirru-html/-/cirru-html-0.2.2.tgz", 91 | "integrity": "sha1-pfAxBX9awJ8ED4H/bUnrAPGTAZk=", 92 | "dev": true, 93 | "requires": { 94 | "cirru-parser": "^0.9.0-1" 95 | } 96 | }, 97 | "cirru-html-js": { 98 | "version": "0.0.3", 99 | "resolved": "https://registry.npmjs.org/cirru-html-js/-/cirru-html-js-0.0.3.tgz", 100 | "integrity": "sha1-G5Ae9BrOedih6xOrnodyIjlv3+g=", 101 | "dev": true, 102 | "requires": { 103 | "cirru-parser": "^0.9.0-1" 104 | } 105 | }, 106 | "cirru-parser": { 107 | "version": "0.9.1", 108 | "resolved": "https://registry.npmjs.org/cirru-parser/-/cirru-parser-0.9.1.tgz", 109 | "integrity": "sha1-i0J+JQoaOrS7Ri/sXYFVjlf8C64=", 110 | "dev": true 111 | }, 112 | "clean-css": { 113 | "version": "2.1.8", 114 | "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-2.1.8.tgz", 115 | "integrity": "sha1-K0sv1g8yRBCWIWriWiH6p0WA3IM=", 116 | "dev": true, 117 | "requires": { 118 | "commander": "2.1.x" 119 | } 120 | }, 121 | "coffee-script": { 122 | "version": "1.12.7", 123 | "resolved": "https://registry.npmjs.org/coffee-script/-/coffee-script-1.12.7.tgz", 124 | "integrity": "sha512-fLeEhqwymYat/MpTPUjSKHVYYl0ec2mOyALEMLmzr5i1isuG+6jfI2j2d5oBO3VIzgUXgBVIcOT9uH1TFxBckw==", 125 | "dev": true 126 | }, 127 | "coffeescript": { 128 | "version": "2.4.1", 129 | "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-2.4.1.tgz", 130 | "integrity": "sha512-34GV1aHrsMpTaO3KfMJL40ZNuvKDR/g98THHnE9bQj8HjMaZvSrLik99WWqyMhRtbe8V5hpx5iLgdcSvM/S2wg==", 131 | "dev": true 132 | }, 133 | "combined-stream": { 134 | "version": "0.0.7", 135 | "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-0.0.7.tgz", 136 | "integrity": "sha1-ATfmV7qlp1QcV6w3rF/AfXO03B8=", 137 | "dev": true, 138 | "optional": true, 139 | "requires": { 140 | "delayed-stream": "0.0.5" 141 | } 142 | }, 143 | "commander": { 144 | "version": "2.1.0", 145 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.1.0.tgz", 146 | "integrity": "sha1-0SG7roYNmZKj1Re6lvVliOR8Z4E=", 147 | "dev": true 148 | }, 149 | "concat-map": { 150 | "version": "0.0.1", 151 | "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", 152 | "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", 153 | "dev": true 154 | }, 155 | "constantinople": { 156 | "version": "2.0.1", 157 | "resolved": "https://registry.npmjs.org/constantinople/-/constantinople-2.0.1.tgz", 158 | "integrity": "sha1-WCn4VvMBqb2xB9k1932OuMzsTHk=", 159 | "dev": true, 160 | "requires": { 161 | "uglify-js": "~2.4.0" 162 | } 163 | }, 164 | "convert-source-map": { 165 | "version": "0.3.5", 166 | "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-0.3.5.tgz", 167 | "integrity": "sha1-8dgClQr33SYxof6+BZZVDIarMZA=", 168 | "dev": true 169 | }, 170 | "core-util-is": { 171 | "version": "1.0.2", 172 | "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", 173 | "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", 174 | "dev": true 175 | }, 176 | "cryptiles": { 177 | "version": "0.2.2", 178 | "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-0.2.2.tgz", 179 | "integrity": "sha1-7ZH/HxetE9N0gohZT4pIoNJvMlw=", 180 | "dev": true, 181 | "optional": true, 182 | "requires": { 183 | "boom": "0.4.x" 184 | } 185 | }, 186 | "css": { 187 | "version": "1.0.8", 188 | "resolved": "https://registry.npmjs.org/css/-/css-1.0.8.tgz", 189 | "integrity": "sha1-k4aBHKgrzMnuf7WnMrHioxfIo+c=", 190 | "dev": true, 191 | "requires": { 192 | "css-parse": "1.0.4", 193 | "css-stringify": "1.0.5" 194 | } 195 | }, 196 | "css-parse": { 197 | "version": "1.0.4", 198 | "resolved": "https://registry.npmjs.org/css-parse/-/css-parse-1.0.4.tgz", 199 | "integrity": "sha1-OLBQP7+dqfVOnB29pg4UXHcRe90=", 200 | "dev": true 201 | }, 202 | "css-stringify": { 203 | "version": "1.0.5", 204 | "resolved": "https://registry.npmjs.org/css-stringify/-/css-stringify-1.0.5.tgz", 205 | "integrity": "sha1-sNBClG2ylTu50pKQCmy19tASIDE=", 206 | "dev": true 207 | }, 208 | "ctype": { 209 | "version": "0.5.3", 210 | "resolved": "https://registry.npmjs.org/ctype/-/ctype-0.5.3.tgz", 211 | "integrity": "sha1-gsGMJGH3QRTvFsE1IkrQuRRMoS8=", 212 | "dev": true, 213 | "optional": true 214 | }, 215 | "decamelize": { 216 | "version": "1.2.0", 217 | "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", 218 | "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=", 219 | "dev": true 220 | }, 221 | "delayed-stream": { 222 | "version": "0.0.5", 223 | "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.5.tgz", 224 | "integrity": "sha1-1LH0OpPoKW3+AmlPRoC8N6MTxz8=", 225 | "dev": true, 226 | "optional": true 227 | }, 228 | "devtools-reloader-station": { 229 | "version": "0.0.4", 230 | "resolved": "https://registry.npmjs.org/devtools-reloader-station/-/devtools-reloader-station-0.0.4.tgz", 231 | "integrity": "sha1-7LNzPyz1GWYWRFY8s+xS5a+7SNY=", 232 | "dev": true, 233 | "requires": { 234 | "ws": "~0.4.31" 235 | } 236 | }, 237 | "dot": { 238 | "version": "1.0.3", 239 | "resolved": "https://registry.npmjs.org/dot/-/dot-1.0.3.tgz", 240 | "integrity": "sha1-+HUL+2sDx2ZOsObLHrTGZBmvlCc=", 241 | "dev": true 242 | }, 243 | "exorcist": { 244 | "version": "0.1.6", 245 | "resolved": "https://registry.npmjs.org/exorcist/-/exorcist-0.1.6.tgz", 246 | "integrity": "sha1-FqTEg39ITQuDlPtjgKE/1rQq+O4=", 247 | "dev": true, 248 | "requires": { 249 | "convert-source-map": "~0.3.3", 250 | "minimist": "0.0.5", 251 | "through2": "~0.4.0" 252 | } 253 | }, 254 | "forever-agent": { 255 | "version": "0.5.2", 256 | "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.5.2.tgz", 257 | "integrity": "sha1-bQ4JxJIflKJ/Y9O0nF/v8epMUTA=", 258 | "dev": true, 259 | "optional": true 260 | }, 261 | "form-data": { 262 | "version": "0.1.4", 263 | "resolved": "https://registry.npmjs.org/form-data/-/form-data-0.1.4.tgz", 264 | "integrity": "sha1-kavXiKupcCsaq/qLwBAxoqyeOxI=", 265 | "dev": true, 266 | "optional": true, 267 | "requires": { 268 | "async": "~0.9.0", 269 | "combined-stream": "~0.0.4", 270 | "mime": "~1.2.11" 271 | }, 272 | "dependencies": { 273 | "async": { 274 | "version": "0.9.2", 275 | "resolved": "https://registry.npmjs.org/async/-/async-0.9.2.tgz", 276 | "integrity": "sha1-rqdNXmHB+JlhO/ZL2mbUx48v0X0=", 277 | "dev": true, 278 | "optional": true 279 | } 280 | } 281 | }, 282 | "fsevents": { 283 | "version": "github:pipobscure/fsevents#7dcdf9fa3f8956610fd6f69f72c67bace2de7138", 284 | "from": "github:pipobscure/fsevents#7dcdf9fa3f8956610fd6f69f72c67bace2de7138", 285 | "dev": true, 286 | "optional": true, 287 | "requires": { 288 | "nan": "~0.8.0" 289 | }, 290 | "dependencies": { 291 | "nan": { 292 | "version": "0.8.0", 293 | "resolved": "https://registry.npmjs.org/nan/-/nan-0.8.0.tgz", 294 | "integrity": "sha1-AiqPpen+hCCWSsH7PclOF/RJ9f0=", 295 | "dev": true, 296 | "optional": true 297 | } 298 | } 299 | }, 300 | "graceful-fs": { 301 | "version": "3.0.12", 302 | "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-3.0.12.tgz", 303 | "integrity": "sha512-J55gaCS4iTTJfTXIxSVw3EMQckcqkpdRv3IR7gu6sq0+tbC363Zx6KH/SEwXASK9JRbhyZmVjJEVJIOxYsB3Qg==", 304 | "dev": true, 305 | "optional": true, 306 | "requires": { 307 | "natives": "^1.1.3" 308 | } 309 | }, 310 | "hawk": { 311 | "version": "1.1.1", 312 | "resolved": "https://registry.npmjs.org/hawk/-/hawk-1.1.1.tgz", 313 | "integrity": "sha1-h81JH5tG5OKurKM1QWdmiF0tHtk=", 314 | "dev": true, 315 | "optional": true, 316 | "requires": { 317 | "boom": "0.4.x", 318 | "cryptiles": "0.2.x", 319 | "hoek": "0.9.x", 320 | "sntp": "0.2.x" 321 | } 322 | }, 323 | "hoek": { 324 | "version": "0.9.1", 325 | "resolved": "https://registry.npmjs.org/hoek/-/hoek-0.9.1.tgz", 326 | "integrity": "sha1-PTIkYrrfB3Fup+uFuviAec3c5QU=", 327 | "dev": true, 328 | "optional": true 329 | }, 330 | "http-signature": { 331 | "version": "0.10.1", 332 | "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-0.10.1.tgz", 333 | "integrity": "sha1-T72sEyVZqoMjEh5UB3nAoBKyfmY=", 334 | "dev": true, 335 | "optional": true, 336 | "requires": { 337 | "asn1": "0.1.11", 338 | "assert-plus": "^0.1.5", 339 | "ctype": "0.5.3" 340 | } 341 | }, 342 | "inherits": { 343 | "version": "2.0.4", 344 | "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", 345 | "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", 346 | "dev": true 347 | }, 348 | "ip-regex": { 349 | "version": "2.1.0", 350 | "resolved": "https://registry.npmjs.org/ip-regex/-/ip-regex-2.1.0.tgz", 351 | "integrity": "sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk=", 352 | "dev": true, 353 | "optional": true 354 | }, 355 | "is-promise": { 356 | "version": "1.0.1", 357 | "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-1.0.1.tgz", 358 | "integrity": "sha1-MVc3YcBX4zwukaq56W2gjO++duU=", 359 | "dev": true 360 | }, 361 | "isarray": { 362 | "version": "0.0.1", 363 | "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", 364 | "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=", 365 | "dev": true 366 | }, 367 | "jade": { 368 | "version": "1.3.1", 369 | "resolved": "https://registry.npmjs.org/jade/-/jade-1.3.1.tgz", 370 | "integrity": "sha1-dIPYSLhxTcUKQNqYsECXkLN0IWs=", 371 | "dev": true, 372 | "requires": { 373 | "character-parser": "1.2.0", 374 | "commander": "2.1.0", 375 | "constantinople": "~2.0.0", 376 | "mkdirp": "~0.3.5", 377 | "monocle": "1.1.51", 378 | "transformers": "2.1.0", 379 | "with": "~3.0.0" 380 | } 381 | }, 382 | "json-stringify-safe": { 383 | "version": "5.0.1", 384 | "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", 385 | "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", 386 | "dev": true, 387 | "optional": true 388 | }, 389 | "less": { 390 | "version": "1.7.5", 391 | "resolved": "https://registry.npmjs.org/less/-/less-1.7.5.tgz", 392 | "integrity": "sha1-TyIM9yiKJ+rKc5325ICKLUwNV1Y=", 393 | "dev": true, 394 | "requires": { 395 | "clean-css": "2.2.x", 396 | "graceful-fs": "~3.0.2", 397 | "mime": "~1.2.11", 398 | "mkdirp": "~0.5.0", 399 | "request": "~2.40.0", 400 | "source-map": "0.1.x" 401 | }, 402 | "dependencies": { 403 | "clean-css": { 404 | "version": "2.2.23", 405 | "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-2.2.23.tgz", 406 | "integrity": "sha1-BZC1R4tRbEkD7cLYm9P9vdKGMow=", 407 | "dev": true, 408 | "optional": true, 409 | "requires": { 410 | "commander": "2.2.x" 411 | } 412 | }, 413 | "commander": { 414 | "version": "2.2.0", 415 | "resolved": "https://registry.npmjs.org/commander/-/commander-2.2.0.tgz", 416 | "integrity": "sha1-F1rUuTF/P/YV8gHB5XIk9Vo+kd8=", 417 | "dev": true, 418 | "optional": true 419 | }, 420 | "minimist": { 421 | "version": "0.0.8", 422 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", 423 | "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", 424 | "dev": true, 425 | "optional": true 426 | }, 427 | "mkdirp": { 428 | "version": "0.5.1", 429 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", 430 | "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", 431 | "dev": true, 432 | "optional": true, 433 | "requires": { 434 | "minimist": "0.0.8" 435 | } 436 | } 437 | } 438 | }, 439 | "lodash": { 440 | "version": "2.4.2", 441 | "resolved": "https://registry.npmjs.org/lodash/-/lodash-2.4.2.tgz", 442 | "integrity": "sha1-+t2DS5aDBz2hebPq5tnA0VBT9z4=", 443 | "dev": true 444 | }, 445 | "mime": { 446 | "version": "1.2.11", 447 | "resolved": "https://registry.npmjs.org/mime/-/mime-1.2.11.tgz", 448 | "integrity": "sha1-WCA+7Ybjpe8XrtK32evUfwpg3RA=", 449 | "dev": true, 450 | "optional": true 451 | }, 452 | "mime-types": { 453 | "version": "1.0.2", 454 | "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-1.0.2.tgz", 455 | "integrity": "sha1-mVrhOSq4r/y/yyZB3QVOlDwNXc4=", 456 | "dev": true, 457 | "optional": true 458 | }, 459 | "minimatch": { 460 | "version": "3.0.4", 461 | "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", 462 | "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", 463 | "dev": true, 464 | "requires": { 465 | "brace-expansion": "^1.1.7" 466 | } 467 | }, 468 | "minimist": { 469 | "version": "0.0.5", 470 | "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.5.tgz", 471 | "integrity": "sha1-16oye87PUY+RBqxrjwA/o7zqhWY=", 472 | "dev": true 473 | }, 474 | "mission": { 475 | "version": "0.0.7", 476 | "resolved": "https://registry.npmjs.org/mission/-/mission-0.0.7.tgz", 477 | "integrity": "sha1-n2AYcVUfZ3bX0TQXwGmFTHLb62w=", 478 | "dev": true, 479 | "requires": { 480 | "chokidar": "~0.8.2", 481 | "cirru-html": "~0.2.2", 482 | "cirru-html-js": "0.0.3", 483 | "clean-css": "~2.1.8", 484 | "coffee-script": "^1.7.1", 485 | "devtools-reloader-station": "0.0.4", 486 | "dot": "~1.0.2", 487 | "exorcist": "^0.1.6", 488 | "jade": "~1.3.1", 489 | "less": "~1.7.0", 490 | "rsyncwrapper": "^0.3.0", 491 | "semver": "~2.3.0", 492 | "shelljs": "~0.2.6", 493 | "uglify-js": "~2.4.13" 494 | }, 495 | "dependencies": { 496 | "shelljs": { 497 | "version": "0.2.6", 498 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.2.6.tgz", 499 | "integrity": "sha1-kEktcv/MgVmXa6umL7D2iE8MM3g=", 500 | "dev": true 501 | } 502 | } 503 | }, 504 | "mkdirp": { 505 | "version": "0.3.5", 506 | "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.3.5.tgz", 507 | "integrity": "sha1-3j5fiWHIjHh+4TaN+EmsRBPsqNc=", 508 | "dev": true 509 | }, 510 | "monocle": { 511 | "version": "1.1.51", 512 | "resolved": "https://registry.npmjs.org/monocle/-/monocle-1.1.51.tgz", 513 | "integrity": "sha1-Iu0W4RLpsFZ2nFzKySDjdSSdicA=", 514 | "dev": true, 515 | "requires": { 516 | "readdirp": "~0.2.3" 517 | } 518 | }, 519 | "nan": { 520 | "version": "1.0.0", 521 | "resolved": "https://registry.npmjs.org/nan/-/nan-1.0.0.tgz", 522 | "integrity": "sha1-riT4hQgY1mL8q1rPfzuVv6oszzg=", 523 | "dev": true 524 | }, 525 | "natives": { 526 | "version": "1.1.6", 527 | "resolved": "https://registry.npmjs.org/natives/-/natives-1.1.6.tgz", 528 | "integrity": "sha512-6+TDFewD4yxY14ptjKaS63GVdtKiES1pTPyxn9Jb0rBqPMZ7VcCiooEhPNsr+mqHtMGxa/5c/HhcC4uPEUw/nA==", 529 | "dev": true, 530 | "optional": true 531 | }, 532 | "node-uuid": { 533 | "version": "1.4.8", 534 | "resolved": "https://registry.npmjs.org/node-uuid/-/node-uuid-1.4.8.tgz", 535 | "integrity": "sha1-sEDrCSOWivq/jTL7HxfxFn/auQc=", 536 | "dev": true, 537 | "optional": true 538 | }, 539 | "oauth-sign": { 540 | "version": "0.3.0", 541 | "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.3.0.tgz", 542 | "integrity": "sha1-y1QPk7srIqfVlBaRoojWDo6pOG4=", 543 | "dev": true, 544 | "optional": true 545 | }, 546 | "object-keys": { 547 | "version": "0.4.0", 548 | "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", 549 | "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=", 550 | "dev": true 551 | }, 552 | "optimist": { 553 | "version": "0.3.7", 554 | "resolved": "https://registry.npmjs.org/optimist/-/optimist-0.3.7.tgz", 555 | "integrity": "sha1-yQlBrVnkJzMokjB00s8ufLxuwNk=", 556 | "dev": true, 557 | "requires": { 558 | "wordwrap": "~0.0.2" 559 | } 560 | }, 561 | "options": { 562 | "version": "0.0.6", 563 | "resolved": "https://registry.npmjs.org/options/-/options-0.0.6.tgz", 564 | "integrity": "sha1-7CLTEoBrtT5zF3Pnza788cZDEo8=", 565 | "dev": true 566 | }, 567 | "promise": { 568 | "version": "2.0.0", 569 | "resolved": "https://registry.npmjs.org/promise/-/promise-2.0.0.tgz", 570 | "integrity": "sha1-RmSKqdYFr10ucMMCS/WUNtoCuA4=", 571 | "dev": true, 572 | "requires": { 573 | "is-promise": "~1" 574 | } 575 | }, 576 | "psl": { 577 | "version": "1.4.0", 578 | "resolved": "https://registry.npmjs.org/psl/-/psl-1.4.0.tgz", 579 | "integrity": "sha512-HZzqCGPecFLyoRj5HLfuDSKYTJkAfB5thKBIkRHtGjWwY7p1dAyveIbXIq4tO0KYfDF2tHqPUgY9SDnGm00uFw==", 580 | "dev": true, 581 | "optional": true 582 | }, 583 | "punycode": { 584 | "version": "2.1.1", 585 | "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", 586 | "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", 587 | "dev": true, 588 | "optional": true 589 | }, 590 | "qs": { 591 | "version": "1.0.2", 592 | "resolved": "https://registry.npmjs.org/qs/-/qs-1.0.2.tgz", 593 | "integrity": "sha1-UKk+K1r2aRwxvOpdrnjubqGQN2g=", 594 | "dev": true, 595 | "optional": true 596 | }, 597 | "readable-stream": { 598 | "version": "1.0.34", 599 | "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", 600 | "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", 601 | "dev": true, 602 | "requires": { 603 | "core-util-is": "~1.0.0", 604 | "inherits": "~2.0.1", 605 | "isarray": "0.0.1", 606 | "string_decoder": "~0.10.x" 607 | } 608 | }, 609 | "readdirp": { 610 | "version": "0.2.5", 611 | "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-0.2.5.tgz", 612 | "integrity": "sha1-xMJ25Sl3riXbUZH+UdAIVQ8V2bs=", 613 | "dev": true, 614 | "requires": { 615 | "minimatch": ">=0.2.4" 616 | } 617 | }, 618 | "recursive-readdir": { 619 | "version": "0.0.2", 620 | "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-0.0.2.tgz", 621 | "integrity": "sha1-C8R9xIOOZG3M+6BQe15X/7/zX3w=", 622 | "dev": true, 623 | "optional": true 624 | }, 625 | "request": { 626 | "version": "2.40.0", 627 | "resolved": "https://registry.npmjs.org/request/-/request-2.40.0.tgz", 628 | "integrity": "sha1-TdZw9pbx5uhC5mtLXoOTAaub62c=", 629 | "dev": true, 630 | "optional": true, 631 | "requires": { 632 | "aws-sign2": "~0.5.0", 633 | "forever-agent": "~0.5.0", 634 | "form-data": "~0.1.0", 635 | "hawk": "1.1.1", 636 | "http-signature": "~0.10.0", 637 | "json-stringify-safe": "~5.0.0", 638 | "mime-types": "~1.0.1", 639 | "node-uuid": "~1.4.0", 640 | "oauth-sign": "~0.3.0", 641 | "qs": "~1.0.0", 642 | "stringstream": "~0.0.4", 643 | "tough-cookie": ">=0.12.0", 644 | "tunnel-agent": "~0.4.0" 645 | } 646 | }, 647 | "rsyncwrapper": { 648 | "version": "0.3.0", 649 | "resolved": "https://registry.npmjs.org/rsyncwrapper/-/rsyncwrapper-0.3.0.tgz", 650 | "integrity": "sha1-bc2b2R9W21k7/daTAkmMXOTomFY=", 651 | "dev": true, 652 | "requires": { 653 | "lodash": "~2.4.1" 654 | } 655 | }, 656 | "semver": { 657 | "version": "2.3.2", 658 | "resolved": "https://registry.npmjs.org/semver/-/semver-2.3.2.tgz", 659 | "integrity": "sha1-uYSPJdbPNjMwc+ye+IVtQvEjPlI=", 660 | "dev": true 661 | }, 662 | "shelljs": { 663 | "version": "0.3.0", 664 | "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz", 665 | "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E=", 666 | "dev": true 667 | }, 668 | "sntp": { 669 | "version": "0.2.4", 670 | "resolved": "https://registry.npmjs.org/sntp/-/sntp-0.2.4.tgz", 671 | "integrity": "sha1-+4hfGLDzqtGJ+CSGJTa87ux1CQA=", 672 | "dev": true, 673 | "optional": true, 674 | "requires": { 675 | "hoek": "0.9.x" 676 | } 677 | }, 678 | "source-map": { 679 | "version": "0.1.34", 680 | "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.34.tgz", 681 | "integrity": "sha1-p8/omux7FoLDsZjQrPtH19CQVms=", 682 | "dev": true, 683 | "requires": { 684 | "amdefine": ">=0.0.4" 685 | } 686 | }, 687 | "string_decoder": { 688 | "version": "0.10.31", 689 | "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", 690 | "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", 691 | "dev": true 692 | }, 693 | "stringstream": { 694 | "version": "0.0.6", 695 | "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", 696 | "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==", 697 | "dev": true, 698 | "optional": true 699 | }, 700 | "through2": { 701 | "version": "0.4.2", 702 | "resolved": "https://registry.npmjs.org/through2/-/through2-0.4.2.tgz", 703 | "integrity": "sha1-2/WGYDEVHsg1K7bE22SiKSqEC5s=", 704 | "dev": true, 705 | "requires": { 706 | "readable-stream": "~1.0.17", 707 | "xtend": "~2.1.1" 708 | } 709 | }, 710 | "tinycolor": { 711 | "version": "0.0.1", 712 | "resolved": "https://registry.npmjs.org/tinycolor/-/tinycolor-0.0.1.tgz", 713 | "integrity": "sha1-MgtaUtg6u1l42Bo+iH1K77FaYWQ=", 714 | "dev": true 715 | }, 716 | "tough-cookie": { 717 | "version": "3.0.1", 718 | "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-3.0.1.tgz", 719 | "integrity": "sha512-yQyJ0u4pZsv9D4clxO69OEjLWYw+jbgspjTue4lTQZLfV0c5l1VmK2y1JK8E9ahdpltPOaAThPcp5nKPUgSnsg==", 720 | "dev": true, 721 | "optional": true, 722 | "requires": { 723 | "ip-regex": "^2.1.0", 724 | "psl": "^1.1.28", 725 | "punycode": "^2.1.1" 726 | } 727 | }, 728 | "transformers": { 729 | "version": "2.1.0", 730 | "resolved": "https://registry.npmjs.org/transformers/-/transformers-2.1.0.tgz", 731 | "integrity": "sha1-XSPLNVYd2F3Gf7hIIwm0fVPM6ac=", 732 | "dev": true, 733 | "requires": { 734 | "css": "~1.0.8", 735 | "promise": "~2.0", 736 | "uglify-js": "~2.2.5" 737 | }, 738 | "dependencies": { 739 | "uglify-js": { 740 | "version": "2.2.5", 741 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.2.5.tgz", 742 | "integrity": "sha1-puAqcNg5eSuXgEiLe4sYTAlcmcc=", 743 | "dev": true, 744 | "requires": { 745 | "optimist": "~0.3.5", 746 | "source-map": "~0.1.7" 747 | } 748 | } 749 | } 750 | }, 751 | "tunnel-agent": { 752 | "version": "0.4.3", 753 | "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.4.3.tgz", 754 | "integrity": "sha1-Y3PbdpCf5XDgjXNYM2Xtgop07us=", 755 | "dev": true, 756 | "optional": true 757 | }, 758 | "uglify-js": { 759 | "version": "2.4.24", 760 | "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-2.4.24.tgz", 761 | "integrity": "sha1-+tV1XB4Vd2WLsG/5q25UjJW+vW4=", 762 | "dev": true, 763 | "requires": { 764 | "async": "~0.2.6", 765 | "source-map": "0.1.34", 766 | "uglify-to-browserify": "~1.0.0", 767 | "yargs": "~3.5.4" 768 | } 769 | }, 770 | "uglify-to-browserify": { 771 | "version": "1.0.2", 772 | "resolved": "https://registry.npmjs.org/uglify-to-browserify/-/uglify-to-browserify-1.0.2.tgz", 773 | "integrity": "sha1-bgkk1r2mta/jSeOabWMoUKD4grc=", 774 | "dev": true 775 | }, 776 | "window-size": { 777 | "version": "0.1.0", 778 | "resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.0.tgz", 779 | "integrity": "sha1-VDjNLqk7IC76Ohn+iIeu58lPnJ0=", 780 | "dev": true 781 | }, 782 | "with": { 783 | "version": "3.0.1", 784 | "resolved": "https://registry.npmjs.org/with/-/with-3.0.1.tgz", 785 | "integrity": "sha1-CDVNpBAkPPYXP7FCuwTmxm+W+FQ=", 786 | "dev": true, 787 | "requires": { 788 | "uglify-js": "~2.4.12" 789 | } 790 | }, 791 | "wordwrap": { 792 | "version": "0.0.2", 793 | "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-0.0.2.tgz", 794 | "integrity": "sha1-t5Zpu0LstAn4PVg8rVLKF+qhZD8=", 795 | "dev": true 796 | }, 797 | "ws": { 798 | "version": "0.4.32", 799 | "resolved": "https://registry.npmjs.org/ws/-/ws-0.4.32.tgz", 800 | "integrity": "sha1-eHphVEFPPJntg8V3IVOyD+sM7DI=", 801 | "dev": true, 802 | "requires": { 803 | "commander": "~2.1.0", 804 | "nan": "~1.0.0", 805 | "options": ">=0.0.5", 806 | "tinycolor": "0.x" 807 | } 808 | }, 809 | "xtend": { 810 | "version": "2.1.2", 811 | "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", 812 | "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", 813 | "dev": true, 814 | "requires": { 815 | "object-keys": "~0.4.0" 816 | } 817 | }, 818 | "yargs": { 819 | "version": "3.5.4", 820 | "resolved": "https://registry.npmjs.org/yargs/-/yargs-3.5.4.tgz", 821 | "integrity": "sha1-2K/49mXpTDS9JZvevRv68N3TU2E=", 822 | "dev": true, 823 | "requires": { 824 | "camelcase": "^1.0.2", 825 | "decamelize": "^1.0.0", 826 | "window-size": "0.1.0", 827 | "wordwrap": "0.0.2" 828 | } 829 | } 830 | } 831 | } 832 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "kotlin-is-like-csharp", 3 | "version": "0.0.1", 4 | "description": "Kotlin is like C#", 5 | "main": "index.js", 6 | "scripts": { 7 | "build": "coffee make.coffee dev", 8 | "test": "echo \"Error: no test specified\" && exit 1" 9 | }, 10 | "author": "Tomi Tuhkanen", 11 | "license": "MIT", 12 | "devDependencies": { 13 | "mission": "0.0.7", 14 | "shelljs": "^0.3.0", 15 | "coffeescript": "^2.4.1" 16 | }, 17 | "repository": { 18 | "type": "git", 19 | "url": "https://github.com/ttu/kotlin-is-like-csharp.git" 20 | }, 21 | "keywords": [ 22 | "kotlin", 23 | "C#" 24 | ], 25 | "bugs": { 26 | "url": "https://github.com/ttu/kotlin-is-like-csharp/issues" 27 | }, 28 | "homepage": "https://github.com/ttu/kotlin-is-like-csharp" 29 | } 30 | --------------------------------------------------------------------------------