Let's pretend we have the following types:
type Message {
text : Option<string>
}
type Update {
msg : Option<Message>
}
How do I match it in one line, like in C# using null-conditional operator i.e update?.msg?.text ?
Is there a way to do it like this?:
match msg, msg.text with
| Some msg, Some txt -> ...
| None -> ...
because I don't want to be writing 2 nested match expressions.
You have two Record types (missing the "=" in your example). To match some variable of Update type, you could do as follows:
type Message = { text : Option<string> }
type Update = { msg : Option<Message> }
let u = {msg = Some({text = Some "text"})}
//all 3 possible cases
match u with
| {msg = Some({text = Some t})} -> t
| {msg = Some({text = None})} -> ""
| {msg = None} -> ""
Is there an equivalent to Java's Wildcards in Flow?
Here's my example code I've been working on as a test:
type InterfaceType = {
var1 : number,
};
type ActualType = InterfaceType & {
var2 : string,
};
type InterfaceGenericType<T : InterfaceType> = {
var3 : T,
}
type ActualGenericType = InterfaceGenericType<ActualType> & {
}
class State<T : InterfaceGenericType<InterfaceType>> {
prop : T;
constructor(arg : T) : State<T> {
this.prop = arg;
return this;
}
}
let actual : ActualType = {
var1: 1,
var2: "two",
};
let actualGeneric : ActualGenericType = {
var3 : actual,
}
let s2 = new State(actualGeneric);
This is the flow error I'm getting:
40: let s2 = new State(actualGeneric);
^ Cannot call `State` with `actualGeneric` bound to `arg` because property `var2` is missing in `InterfaceType` [1] but exists in object type [2] in property `var3`.
References:
20: class State<T : InterfaceGenericType<InterfaceType>> {
^ [1]
7: type ActualType = InterfaceType & {
^ [2]
I know I can get around the issue by doing:
class State<I : InterfaceType, T : InterfaceGenericType<I>> {
but I'm trying to not have to declare both types.
We can trim down your code a bit to remove the class:
type InterfaceType = { var1: number };
type ActualType = InterfaceType & { var2: string, };
type InterfaceGenericType<T : InterfaceType> = {
var3: T,
};
let actual: ActualType = {
var1: 1,
var2: "two",
};
let actualGeneric: InterfaceGenericType<ActualType> = {
var3: actual,
};
let v: InterfaceGenericType<InterfaceType> = actualGeneric;
I can't speak to Java since I don't know it, but I can tell you how to fix this. If we look at the error for this code:
17: let v: InterfaceGenericType<InterfaceType> = actualGeneric;
^ Cannot assign `actualGeneric` to `v` because property `var2` is missing in `InterfaceType` [1] but exists in object type [2] in type argument `T` [3].
References:
17: let v: InterfaceGenericType<InterfaceType> = actualGeneric;
^ [1]
2: type ActualType = InterfaceType & { var2: string, };
^ [2]
4: type InterfaceGenericType<T : InterfaceType> = {
^ [3]
The core issue is that v's type InterfaceGenericType<InterfaceType> would for example allow you to do
v.var3 = { var1: 42 };
because that is a valid InterfaceType object. That isn't a valid ActualType object, but by assigning actualGeneric to v, you've essentially erased that type information, which means that if your code were allowed as-is, the assignment would corrupt your actualGeneric object's type.
The fix for this is to tell Flow that the var3 property is read-only, by changing
var3: T,
to be
+var3: T,
I am writing SML program to update records in a list.For example, I have type person_name.
type person_name = {fname:string, lname:string, mname:string}
Then I have person_bio which has person_name embedded in it.
type person_bio = {age:real, gender:string, name:person_name, status:string}
Next I have employee which has person_bio.
type employee = {p:person_bio, payrate:real, whours:real} list;
Now, I have to define function 'updateLastName' by passing the first name.
As of now, created one record 'e1' with below data.
{p={age=40.0,gender="M",name{fname="rob",lname="sen",mname=""},status="M"},
payrate=30.0,whours=10.0}
But I am facing challenge to traverse the list and then updating one field in record.
fun updateLastName(x:string,l:employee)=
if (L=[]) then []
else if (x= #fname(#name(#p hd l)) //cheking name of 1st record in list
//not getting how to update,this kind of line did not work
#fname(#name(#p hd l) = "abc"
else updateLastName(x,tl(l)); // hope this is right
Please suggest.
You have stumbled upon something difficult: Updating a deeply nested record.
For records you have getters, so #fname (#name (#p employee)) gets the field that you're checking against to know that this is the employee whose last name you are going to update. But records don't grant you equivalent setters, so you have to make those. If you're curious, lenses (Haskell) are a general way to solve this, but I don't know of any implementation of lenses for Standard ML.
I'll go ahead and remove the list part in your employee type; you should probably want an employee list if you want multiple employees modelled, rather than to say that an employee is multiple persons.
type person_name = { fname:string, lname:string, mname:string }
type person_bio = { age:real, gender:string, name:person_name, status:string }
type employee = { p:person_bio, payrate:real, whours:real }
val name1 = { fname = "John", lname = "Doe", mname = "W." } : person_name
val bio1 = { age = 42.0, gender = "M", name = name1, status = "?" } : person_bio
val my_employee1 = { p = bio1, payrate = 1000.0, whours = 37.0 } : employee
val name2 = { fname = "Freddy", lname = "Mercury", mname = "X." } : person_name
val bio2 = { age = 45.0, gender = "M", name = name2, status = "?" } : person_bio
val my_employee2 = { p = bio2, payrate = 2000.0, whours = 37.0 } : employee
val my_employees = [ my_employee1, my_employee2 ] : employee list
As for the setters (the ones that you could automatically derive using lenses),
fun setP (p : person_bio, e : employee) =
{ p = p
, payrate = #payrate e
, whours = #whours e } : employee
fun setName (name : person_name, pb : person_bio) =
{ age = #age pb
, gender = #gender pb
, name = name
, status = #status pb } : person_bio
fun setLname (lname, pn : person_name) =
{ fname = #fname pn
, lname = lname
, mname = #mname pn } : person_name
you can compose these, e.g. like:
- setP (setName (setLname ("Johnson", #name (#p my_employee1)), #p my_employee1), my_employee1)
> val it =
{p =
{age = 42.0, gender = "M",
name = {fname = "John", lname = "Johnson", mname = "W."},
status = "?"}, payrate = 1000.0, whours = 37.0} :
{p :
{age : real, gender : string,
name : {fname : string, lname : string, mname : string},
status : string}, payrate : real, whours : real}
Or you can split that line a little apart to make it more readable:
fun updateLname (fname, lname, employees) =
let fun update employee =
if #fname (#name (#p employee)) = fname
then let val new_name = setLname (lname, #name (#p employee))
val new_bio = setName (new_name, #p employee)
val new_employee = setP (new_bio, employee)
in new_employee end
else employee
in List.map update employees
end
Trying this out:
- updateLname ("Freddy", "Johnson", my_employees);
> val it =
[{p = ... {fname = "John", lname = "Doe", mname = "W."}, ... },
{p = ... {fname = "Freddy", lname = "Johnson", mname = "X."}, ... }]
- updateLname ("John", "Johnson", my_employees);
> val it =
[{p = ... {fname = "John", lname = "Johnson", mname = "W."}, ... },
{p = ... {fname = "Freddy", lname = "Mercury", mname = "X."}, ... }]
Depending on your situation, references may be appropriate here.
For any values you may need to change, you can make them a reference, i.e.
type person_name = {fname:string, lname:string ref, mname:string}
type person_bio = {age:real, gender:string, name:person_name, status:string}
fun change_lname(new_lname: string, bio: person_bio) = (#lname (#name bio)) := new_lname
val p1 = ...
print !(#lname (#name p1)) ==> LastName1
change_lname("LastName2", p1)
print !(#lname (#name p1)) ==> LastName2
If you plan on modifying data in a record a lot, it's probably a good idea to make it a reference so that your program is not rewriting memory every time it needs to change one value (though in many situations the compiler/interpreter will be able to optimize this). It also saves you from having to rewrite setter functions if the signature of your record changes. The downside is that you'll be introducing complexity into your program by using references.
For example, in the above code, we're not actually modifying p1's last name, instead p1 and a copy (passed to the function) both point to the same string, and we modify that string in the function. At no point are we actually changing any of the data in either record, we're only changing data that the records point to. It's a subtle difference, and it doesn't really make a difference in this example, but it can lead to strange bugs that are hard to debug.
I have this valid flow-js definition for mysql :
declare type QueryResults = Array<Object> &{
insertId?: string | number,
affectedRows?: number,
changedRows?: number
};
And I've trying to create a variable that would be consistent with this definition. (You can try it here):
/* #flow */
type A = Array<Object>
type B = {
insertId?: string | number,
affectedRows?: number,
changedRows?: number
}
type C = A & B;
let a1: A = []
let a2: A = [{}]
let b1: B = {}
let b2: B = {insertId: 3}
let c: C
c = [] // Error, not complient with B
c.insertId = 5 // Error, not complient with A
Well, I just found out see here
c = {}.extend([])
Serialization can fails with a class object created containing __pairs:
test = torch.class('test')
function test:__init()
self.data = {}
end
function test:__pairs(...)
return pairs(self.data, ...)
end
function test:get_data()
print(self.data)
end
a = test.new()
a.data = {"asdasd"}
b = torch.serialize(a)
c = torch.deserialize(b)
print(torch.typename(c))
print(c:get_data())
The following returns:
test
nil
The engine behind the torch.serialization is located in the File-class. The File:writeObject us the key function. For the above example the action for a Torch class starts at line 201 with the:
elseif typeidx == TYPE_TORCH then
The type is identified in the File:isWritableObject.
One could probably implement the metatable function write but in the above example the problem was non-torch metatable function __pairs that should be __pairs__ (see torch.getmetatable):
test = torch.class('test')
function test:__init()
self.data = {}
end
function test:__pairs__(...)
return pairs(self.data, ...)
end
function test:get_data()
print(self.data)
end
a = test.new()
a.data = {"asdasd"}
b = torch.serialize(a)
c = torch.deserialize(b)
print(torch.typename(c))
print(c:get_data())
This gives the expected:
test
{
1 : "asdasd"
}