I have a class in Chapel, and I want to let it control it's own print statement. So for instance
class Noob {
var name: string, experience:int;
// constructor
}
Later, I want to do something like
me = new Noob('brian', 0)
writeln(me)
> Hi, my name is Brian and I have 0 years experience
It looks like this should help, but I can't quite get the readThis() method to work.
You'll need a writeThis(f) method to override the default writeln behavior for an object:
class Noob {
var name: string, experience:int;
proc writeThis(f) {
f.writef('Hi, my name is %s and I have %t years experience',
this.name, this.experience);
}
}
var noob = new Noob('ben', 2);
writeln(noob);
You can also use the <~> operator in a readWriteThis(f) method to handle both reading and writing of the object:
class Noob {
var name: string, experience:int;
proc readWriteThis(f) {
f <~> 'Hi, my name is %s and I have %t years experience'.format(this.name, this.experience);
}
}
Related
Say I have the following:
object Foo {
fun Int.negate() = -this
fun negateInt(n: Int) = -n
}
I can call the extension method negate on Foo by using with:
fun main() {
println(with(Foo) { 5.negate() }) // prints -5
}
I can call the other method by calling it on the Foo object:
fun main2() {
println(Foo.negateInt(5)) // prints -5
}
I think with(Foo) { 5.negate() } is syntactically a bit on the heavy side compared to Foo.negateInt(5) when the body is just a single invocation. I can't find a more compact way to perform this call though. I had hoped that I could do Foo::negate to get a function (Int) -> Int where this has been lifted to be an argument, just like I can do for normal non-extension methods. For example, val f = Int::toString will give me a function (Int) -> String such that f(42) is equivalent to 42.toString(). If I could do that, then I could write (Foo::negate)(5) which is still heavy, but less heavy than with(Foo) { ... }.
Is there really no way to explicitly refer to an extension method defined as a member?
Yes, you can refer directly. For that you just need to import the extension function to your current package:
import Foo.negate
Then in your code you can do:
println(5.negate())
Maybe this is the best you can get
class Bar(val offset: Int) { fun Int.negate() = offset - this }
fun main() {
val bar = Bar(42)
bar.run { 5.negate() }
}
Would you please let me know the reason why both of
var name: String
var age: Int
have the following error message:
property must be initialized or abstract.
I would like to declare them without initialization
Main:
data class Person(val _name: String,val _age: Int) {
var name: String
var age: Int
init {
name: String = _name.capitalize()
age: Int = _age * 10;
println("the name is: $name")
println("the age is: $age")
}
/*override fun toString(): String {
return "$name is $age years old."
}*/
}
It also says: Unexpected tokens (use ';' to separate expressions on the same line) and that within the init-function.
So actually the message you showed is only one of many problems. As the init-function is already corrupt, the compiler can not see the assignment and therefore it marks your declaration itself as an error too.
Just omit the "type declaration" (actually it's just code that shouldn't be there ;-)) on the assignment in the init and it will compile:
data class Person(val _name: String,val _age: Int) {
var name: String
var age: Int
init {
name = _name.capitalize()
age = _age * 10;
println("the name is: $name")
println("the age is: $age")
}
/*override fun toString(): String {
return "$name is $age years old."
}*/
}
some background:
val (name, age) = person
This syntax is called a destructuring declaration. It creates multiple variables (correction, creates multiple values) at at the same time.
Destructuring declarations also work in for-loops: when you say:
for ((a, b) in collection) { ... }
Lets take a look at a list item i have:
#Parcelize
data class MyModel(
var name: String = "",
var is_locked: Boolean = true,
var is_one_size: Boolean = false,
) : Parcelable
and now i have obtained a list of "MyModel" classes and i am trying to loop over them like this:
private fun initMyModelList(model: MutableList<MyModel>) {
//i want to access is_locked from here with destruction but i cant ? IDE telling me the type is an int but its clearly defined as a Boolean
for((is_locked) in model){
//what i want to do in here is access the is_locked var of the model list and change all of them in a loop. im trying to use Destructuring in loop as a conveience. why is it not working ?
//how can i make the call signature look like this--- > is_locked = true instad of model.is_locked =true
}
}
all i want to do is be able to call is_locked = true instead of model.is_locked = true within the loop. how can this be done ?
This syntax is called a destructuring declaration. It creates multiple variables at at the same time.
It doesn't create multiple variables, it captures multiple values. You're working with values, not references, as your source tells further:
A destructuring declaration is compiled down to the following code:
val name = person.component1()
val age = person.component2()
Closest to what you want would be this custom extension function:
inline fun <E> Iterable<E>.withEach(block: E.() -> Unit) {
forEach {
it.block()
}
}
Use like so:
model.withEach {
is_locked = true
}
Before you ask the obligatory question "why isn't this included in stdlib?" consider that functional style programming typically is about transforming immutable types. Basically, what I did here was encourage a bad habit.
Basically, it isn't possible, cause your code is compiled to something like:
for (m in models) {
val is_locked = m.component1()
...
}
Which means that you create a local property which cannot be reassigned. But you can do something like this:
for (m in model) {
with(m) {
is_locked = true
}
}
Yep, it isn't perfect, but it can be improved with extension methods:
fun <T> List<T>.forEachApply(block: T.() -> Unit) {
forEach(block)
}
private fun initMyModelList(model: MutableList<MyModel>) {
model.forEachApply {
is_locked = true
}
}
You can use destructuring in a loop just fine as read-only values.
data class Stuff(val name: String, val other: String)
fun doStuff() {
val stuff = Stuff("happy", "day")
val stuffs = listOf(stuff)
for ((name) in stuffs) {
println(name)
}
}
Running that method prints "happy" to the console. Baeldung shows an example of using it here.
It's best practice for data classes to be immutable, so I would try to rewrite your data class to be immutable. The .copy function will let you copy your data class but with new, different values.
In my code I often have to copy data from json to instantiate class in constructor.
function append(dst, src) {
for (let key in src) {
if (src.hasOwnProperty(key) {
dst[key] = src[key];
}
}
};
export class DataClass {
id: number;
title: string;
content: string;
img: null | string;
author: string;
// no methods, just raw data from API
}
export class AdoptedClass1 extends DataClass {
// has same fields as DataClass
showcase: string;
constructor (data: DataClass) {
append(data, this);
// do some stuff
}
}
// similar code for AdoptedClass2
I'm wondering if I can replace append function call in constructor with object spread operator
For your need I'll prefer to use Object.assign(this, data) over your custom made append function. Nevertheless have a look at the documentation to understand the limitation of it.
Back to your main question: it is not possible to use the spread operator to do what you want. Many people are interested in that feature but it has been put on hold as you can see here.
To get closer of what you ask we can refactor your code a little:
export class DataClass {
id: number
title: string
content: string
img: null | string
author: string
constructor(data: DataClass) {
Object.assign(this, data)
}
}
export class AdoptedClass1 extends DataClass {
showcase: string
constructor (data: DataClass) {
super(data)
// do some stuff
}
}
By simply adding the constructor to the data class you will be allowed to use super(data) in children and IMHO the code will be a lot cleaner.
You can use object spread operator by replacing this line:
append(data,this)
with this line
data = {...data, ...this};
Suppose I have this class and interface
class User {
name: string;
age: number;
isAdmin: boolean;
}
interface IUser {
name: string;
age: number;
}
And then I get this json object from somewhere
const data = {
name: "John",
age: 25,
isAdmin: true
}
I want to subset data using IUser and remove the isAdmin property like this
let user = subset<IUser>(data);
// user is now { name: "John", age: 25 }
// can safely insert user in the db
My question is how do I implement that function in TypeScript?
function subset<T>(obj: object) {
// keep all properties of obj that are in T
// keep, all optional properties in T
// remove any properties out of T
}
There's no way to do that which is better than:
function subset(obj: IUser) {
return {
name: obj.name,
age: obj.age
}
}
The typescript interfaces don't exist at runtime (which is when subset is invoked) so you cannot use the IUser interface to know which properties are needed and which aren't.
You can use a class which does "survive" the compilation process but:
class IUser {
name: string;
age: number;
}
Compiles to:
var IUser = (function () {
function IUser() {
}
return IUser;
}());
As you can see, the properties aren't part of the compiled output, as the class members are only added to the instance and not to the class, so even a class won't help you.
You can use decorator and metadata (more on that here) but that sounds like an overkill for your scenario.
Another option for a more generic subset function is:
function subset<T>(obj: T, ...keys: (keyof T)[]) {
const result = {} as T;
keys.forEach(key => result[key] = obj[key]);
return result;
}
let user1 = subset(data, "name", "age");
let user2 = subset(data, "name", "ag"); // error: Argument of type '"ag"' is not assignable to parameter of type '"name" | "age" | "isAdmin"'