I'm trying to load all my Categories from the database and then map them to a Map (dictionary?), however when I use the following code:
[<StructuralComparison>]
type Category = {
mutable Id: string;
Name: string;
SavePath: string;
Tags: ResizeArray<Tag> }
let categories = session.Query<Category>()
|> Seq.map(fun z -> (z,0))
|> Map.ofSeq
it simply throws an error that says:
The struct, record or union type
'Category' has the
'StructuralComparison' attribute but
the component type 'ResizeArray'
does not satisfy the 'comparison'
constraint
I have absolutely no clue about what to do, so any help is appreciated!
F# is rightly complaining that ResizeArray<_> doesn't support structural comparison. ResizeArray<_> instances are mutable and don't support structural comparison, and you therefore can't use them as fields of records which are used as keys to Map<_,_>s (which are sorted by key). You have a few options:
Have Tags use a collection type that does support structural comparison (such as an immutable Tag list).
Use [<CustomComparison>] instead of [<StructuralComparison>], which requires implementing IComparable.
Use a mutable dictionary (or another relevant collection type) instead of a Map. Try using the dict function instead of Map.ofSeq, for instance.
The problem here is that by adding StructuralComparison attribute to the type Category you've asked F# to use structural comparison when comparing instances of Category. In short it will compare every member individually to see if they are equal to determine if two instances of Category are equal.
This puts an implicit constraint on every member of Category to themselves be comparable. The type ResizeArray<Tag> is generating an error because it's not comparable. This is true for most collection types.
In order to get rid of this error you'll need to make the ResizeArray<T> type comparable or choose a different key for the Map. Don has a great blog post that goes into this topic in depth and provides a number of different ways to achieve this. It's very much worth the read
http://blogs.msdn.com/b/dsyme/archive/2009/11/08/equality-and-comparison-constraints-in-f-1-9-7.aspx
Related
I'm trying to write the derivation expression for the sum of a to many relationship attribute.
I have an item and a group, the item has a price and total price (amount * price).
I want to write an expression for the total price for the group as the sum of its components.
When I build I get the error
error: Misconfigured Property: LAEItemGroup.totalPrice key path
“items.#sum.totalPrice” uses an operator as an intermediate
component
according to the documentation and the WWDC 2019 Making Apps with Core Data it should be possible to get the sum on a to many relationship.
Could someone please help me find the correct syntax or way to do so.
As a work around I tried to write a var that worked in that class as so
#objc
public var totalPrice: Double {
value(forKeyPath: "items.#sum.totalPrice") as? Double ?? 0
}
so why the KeyPath value works but not in the model editor?
I just finished a WWDC Core Data lab with Rishi who helped me with this! You should use sum:(items.totalPrice) instead of the .#sum syntax. The parentheses syntax can also be used for some other functions (e.g. count:(items) (the number of items in the to-many relationship) or max:(items.createdAt) (the date of the most recent item)).
I've now had an opportunity to check. It seems the format used by the model editor is for the aggregate operator to be at the end of the expression (which as you point out, is different from the format used in other expressions):
items.totalPrice.#sum
Use items.totalPrice.#sum as the derived property's expression in Xcode's model editor.
This only looks to work for numeric types though? I have a property maxDate with a derived property expression of
items.createdAt.#max
It compiles but throws an error at runtime:
'NSInvalidArgumentException', reason: 'currently unsupported (too many steps)
Where Date is the data type for createdAt
I want a function that takes a list of lists. It should sort this list of lists, regardless of its type, by the length of each list within it.
I thought I could achieve this by using the function below, but I am getting type errors. X is not a subtype of Y. From my understanding, using dynamic means it can take any type, so what am I doing wrong?
List<List<dynamic>> sortByLength(List<List<dynamic>> lss) {
final newLss = List.from(lss);
return newLss..sort((a, b) => a.length.compareTo(b.length));
}
Your problem is that you accidentally converted a List<List<dynamic>> to a List<dynamic> and then tried to return that as a List<List<dynamic>> without a cast.
final newLss = List.from(lss);
This makes two mistakes:
It uses List.from instead of List.of (or instead of lss.toList() or [...lss]).
It does not specify an explicit type for the List.
Combined, those mistakes give newLss a type of List<dynamic>. Attempting to return newLss then fails because converting List<dynamic> to List<T> requires using List.cast to change the element type.
Is there a noticeable difference between the two?
var example = ["some","content",11,45,true];
List example = ["some","content",11,45,true];
With var example the type (static and runtime) for example will be inferred from the assigned value ["some","content",11,45,true] which will be List (or actually List<dynamic>)
With List example the type will not be inferred but the explicitely provided type List (or actually List<dynamic> if no generic type is provided) will be used instead.
For var example = ["some","content","11","45","true"]; the inferred type would be List<String>.
As far as I know and as simple as I can be;
List is a data type just like some other built-in types in Dart such as String, int and bool. When you initialize a variable using List, the assigned value must be of List type. i.e. You cannot do this
List example = "sometext";
because you're trying to assign a String value to a List variable or object here.
Whereas, var is a way to declare a variable without specifying its type. For var will accept all kind of data types.
Is there a noticeable difference between the two?
var example = ["some","content",11,45,true];
List example = ["some","content",11,45,true];
Both the methods of declaration have same effect unless you expect to assign a value to example with type(s) other than List during it's lifetime. i.e If you're looking to assign an int or double or string or whatever value to example in future use the first method else you can use any one of them.
I am looking for a data structure that can mimic an Object or a struct. Really, just some compact way to pass around different types of variables. Currently I am using a tuple but referencing various parts of the tuple is less pleasant than I would like. Currently I've just created aliases that represent the various locations in the tuple:
alias AuxClass = tuple[str,str,list[int],list[int],Dec];
int ACLS = 0;
But I've had to restructure this tuple and thus had to change the indexing. Is there something I can use here that I've missed or perhaps a feature coming in the future?
Thanks!
Please take a look at the algebraic data types feature:
http://tutor.rascal-mpl.org/Rascal/Rascal.html#/Rascal/Declarations/AlgebraicDataType/AlgebraicDataType.html
You can create a constructor to represent the type of data that you are trying to define above, similar to what you would do with a struct, and give each element in the constructor a field name:
data AuxClass = auxClass(str f1, str f2, list[int] f3, list[int] f4, Dec f5)
You can then create new instances of this just using the constructor name and providing the data:
a = auxClass("Hello", "World", [1,2,3], [4,5,6], D1) (where D1 is a Dec).
Once you have an instance, you can access information using the field names:
a.f1 // which equals "Hello"
a.f3 // which equals [1,2,3]
size(a.f3) // which currently equals 3
and you can update information using the field names:
a.f2 = "Rascal"
a.f4 = a.f4 + 7 // f4 is now [4,5,6,7]
Algebraic data types are actually quite flexible, so there is a lot you can do with them beyond this. Feel free to look through the documentation and ask questions here.
I have heard that specifying records through tuples in the code is a bad practice: I should always use record fields (#record_name{record_field = something}) instead of plain tuples {record_name, value1, value2, something}.
But how do I match the record against an ETS table? If I have a table with records, I can only match with the following:
ets:match(Table, {$1,$2,$3,something}
It is obvious that once I add some new fields to the record definition this pattern match will stop working.
Instead, I would like to use something like this:
ets:match(Table, #record_name{record_field=something})
Unfortunately, it returns an empty list.
The cause of your problem is what the unspecified fields are set to when you do a #record_name{record_field=something}. This is the syntax for creating a record, here you are creating a record/tuple which ETS will interpret as a pattern. When you create a record then all the unspecified fields will get their default values, either ones defined in the record definition or the default default value undefined.
So if you want to give fields specific values then you must explicitly do this in the record, for example #record_name{f1='$1',f2='$2',record_field=something}. Often when using records and ets you want to set all the unspecified fields to '_', the "don't care variable" for ets matching. There is a special syntax for this using the special, and otherwise illegal, field name _. For example #record_name{record_field=something,_='_'}.
Note that in your example you have set the the record name element in the tuple to '$1'. The tuple representing a record always has the record name as the first element. This means that when you create the ets table you should set the key position with {keypos,Pos} to something other than the default 1 otherwise there won't be any indexing and worse if you have a table of type 'set' or 'ordered_set' you will only get 1 element in the table. To get the index of a record field you can use the syntax #Record.Field, in your example #record_name.record_field.
Try using
ets:match(Table, #record_name{record_field=something, _='_'})
See this for explanation.
Format you are looking for is #record_name{record_field=something, _ = '_'}
http://www.erlang.org/doc/man/ets.html#match-2
http://www.erlang.org/doc/programming_examples/records.html (see 1.3 Creating a record)