Implementing IList<T> in F# - collections

I have looked all over SO and internet but still cannot figure out what I'm missing and how to implement generic IList interface in F# for such type:
type OrCondition() as self =
inherit Condition()
member val Conditions: List<Condition> = new List<Condition>() with get, set
interface IList<Condition> with
member this.Item
with get(index) = self.Conditions.[index]
and set(index)(value) = self.Conditions.[index] <- value
member this.IndexOf item = self.Conditions.IndexOf(item)
member this.Insert(index, item) = self.Conditions.Insert(index, item)
member this.RemoveAt(index) = self.Conditions.RemoveAt(index)
member this.Count with get() = self.Conditions.Count
member this.IsReadOnly with get() = false
member this.Add(item) = self.Conditions.Add(item)
member this.Clear() = self.Conditions.Clear()
member this.Contains(item) = self.Conditions.Contains(item)
member this.CopyTo(conditions, index) = self.Conditions.CopyTo(conditions, index)
member this.Remove(item) = self.Conditions.Remove(item)
member this.GetEnumerator() = (Seq.cast<Condition> self.Conditions).GetEnumerator()
Right now compiler complains that I haven't implemented Collections.IEnumerable.GetEnumerator() and I know it, but I really don't know how to do it.
Update: Final result below. Great thanks to Mr. Garland. Also, worth to point out that I have referenced only System.Collections.Generic and forgot to reference System.Collection when non-generic version of IEnumerable reside. So sad ReSharper doesn't support F#.
open System.Collections
open System.Collections.Generic
type OrCondition() as self =
inherit Condition()
member val Conditions = new List<Condition>() with get, set
interface IList<Condition> with
member this.Item
with get(index) = self.Conditions.[index]
and set(index)(value) = self.Conditions.[index] <- value
member this.IndexOf item = self.Conditions.IndexOf(item)
member this.Insert(index, item) = self.Conditions.Insert(index, item)
member this.RemoveAt(index) = self.Conditions.RemoveAt(index)
member this.Count with get() = self.Conditions.Count
member this.IsReadOnly with get() = false
member this.Add(item) = self.Conditions.Add(item)
member this.Clear() = self.Conditions.Clear()
member this.Contains(item) = self.Conditions.Contains(item)
member this.CopyTo(conditions, index) = self.Conditions.CopyTo(conditions, index)
member this.Remove(item) = self.Conditions.Remove(item)
member this.GetEnumerator() = self.Conditions.GetEnumerator() :> IEnumerator<Condition>
member this.GetEnumerator() = self.Conditions.GetEnumerator() :> IEnumerator

This is because IList has two GetEnumerator methods, one for the IEnumerable<T> interface and one for the non-generic IEnumerable interface.
You could add this member to implement IEnumerable
member this.GetEnumerator() = (this.Conditions :> IEnumerable).GetEnumerator()

The simplest way is
interface System.Collections.IEnumerable with
member this.GetEnumerator() = this.Conditions.GetEnumerator() :> _

Related

Cannot get hash map value as ArrayList

I have a data class called Product and its object are stored in an ArrayList.
Then I am creating an hashmap val prodInCartMap: HashMap<String, ArrayList<Product> = HashMap()
I am adding the array list to the hash map prodInCartMap["prodInCart"] = list, where list is val list: ArrayList<Product> = ArrayList()
Then I am uploading the data to Cloud Firestore.
When I am getting the data this is the code:
fun getHashMapActivity(uid: String, activity: CartActivity){
FirebaseFirestore.getInstance().collection(Constants.CART_COLLECTION)
.document(uid)
.get()
.addOnSuccessListener {
val map: HashMap<String, Any> = it.data as HashMap<String, ArrayList<Product>>
val temp = map["prodInCart"] as ArrayList<Product>
for (i in temp.indices){
val product: Product = temp[i] as Product
}
}
}
While executing the for loop I get this error:
java.lang.ClassCastException: java.util.HashMap cannot be cast to
com.example.grocerystore.models.Product
Why is it still a hash map even after converting it into ArrayList?
Here is the screenshot of my database:
Whenever I hover my mouse over: val cart: HashMap<String, ArrayList<Product>> = it.data as HashMap<String, ArrayList<Product>>
I get this warning:
Unchecked cast: (Mutable)Map<String!, Any!>? to
kotlin.collections.HashMap<String, ArrayList /* =
ArrayList /> / = java.util.HashMap<String,
ArrayList> */
My product class:
data class Product(
var prod_name: String = "",
var prod_image: Int = -1,
var prod_desc: String = "",
var prod_price: Int = -1,
var prod_tags: ArrayList<String> = ArrayList(),
var kgOrPack: Boolean = true //true: pack, false: kg
): Serializable
You are getting the following error:
java.lang.ClassCastException: java.util.HashMap cannot be cast to com.example.grocerystore.models.Product
Because you are trying to loop through an ArrayList that contains "HashMap" objects and not "Product" objects. Since there is no inheritance relationship between these classes, the cast is actually not possible, hence the error. There are two ways in which you can solve this.
The hard way is to iterate the HashMap objects and create "Product" objects yourself, or, the easy way is to map the array of Product objects into a List as explained in the following article:
How to map an array of objects from Cloud Firestore to a List of objects?

How to use Entity Framework in-memory database in an F#?

I am trying to use Entity Framework Core with F# with the in memory database for a very simple use case:
open System
open Microsoft.EntityFrameworkCore
type Position = {
X: double
Y: double
}
type Airport = {
Id: Guid
Name: string
Position: Position
}
type MyContext =
inherit DbContext
new() = { inherit DbContext() }
new(options: DbContextOptions<MyContext>) = { inherit DbContext(options) }
override __.OnConfiguring optionsBuilder =
if optionsBuilder.IsConfigured <> false then
optionsBuilder.UseInMemoryDatabase("database_name") |> ignore
[<DefaultValue>]
val mutable airports: DbSet<Airport>
member x.Airports
with get() = x.airports
and set value = x.airports <- value
module AirportRepository =
let getAirport id =
use context = new MyContext()
query {
for airport in context.Airports do
where (airport.Id = id)
select airport
exactlyOne
} |> (fun x -> if box x = null then None else Some x)
let addAirport (entity: Airport) =
use context = new MyContext()
context.Airports.Add(entity) |> ignore
context.SaveChanges true |> ignore
[<EntryPoint>]
let main argv =
let airport = {
Id = Guid.NewGuid()
Name = "Michelle"
Position = {
X = 42.0
Y = 42.0
}
}
AirportRepository.addAirport airport
0
but it does not work and throw the following exception:
Unhandled Exception: System.InvalidOperationException: No database provider has been configured for this DbContext. A provider can be configured by overriding the DbContext.OnConfiguring method or by using AddDbContext on the application se
rvice provider. If AddDbContext is used, then also ensure that your DbContext type accepts a DbContextOptions<TContext> object in its constructor and passes it to the base constructor for DbContext.
at Microsoft.EntityFrameworkCore.Internal.DbContextServices.Initialize(IServiceProvider scopedProvider, IDbContextOptions contextOptions, DbContext context)
at Microsoft.EntityFrameworkCore.DbContext.get_InternalServiceProvider()
at Microsoft.EntityFrameworkCore.DbContext.get_DbContextDependencies()
at Microsoft.EntityFrameworkCore.DbContext.EntryWithoutDetectChanges[TEntity](TEntity entity)
at Microsoft.EntityFrameworkCore.DbContext.SetEntityState[TEntity](TEntity entity, EntityState entityState)
at Program.AirportRepository.addAirport(Airport entity) in C:\Users\eperret\RiderProjects\FSharpCore\FSharpCore\Program.fs:line 43
at Program.main(String[] argv) in C:\Users\eperret\RiderProjects\FSharpCore\FSharpCore\Program.fs:line 56
How can I make it work, the OnConfiguring override is present so I am not really what I am missing here.
At a guess, I'm wondering if this line is the culprit:
if optionsBuilder.IsConfigured <> false then
optionsBuilder.UseInMemoryDatabase("database_name") |> ignore
The OnConfiguring should only be called by EFCore once per DbContext instance, so you may not need the check for IsConfigured here. Try removing that if branch and try again?
There was a few issues:
As pointed out by Matthew Abbott, the OnConfiguring method was not properly implemented and needed to check if was not already configured (and not the opposite like what I initially did)
It seems that I needed to have only simple types in my entity definition and no complex types (cause there would be considered as another entity
The solution:
open System
open Microsoft.EntityFrameworkCore
[<CLIMutable>]
type Airport = {
Id: Guid
Name: string
X: double
Y: double
}
type MyContext =
inherit DbContext
new() = { inherit DbContext() }
new(options: DbContextOptions<MyContext>) = { inherit DbContext(options) }
override __.OnConfiguring optionsBuilder =
if optionsBuilder.IsConfigured <> true then
optionsBuilder.UseInMemoryDatabase("database_name") |> ignore
[<DefaultValue>]
val mutable airports: DbSet<Airport>
member x.Airports
with get() = x.airports
and set value = x.airports <- value
module AirportRepository =
let getAirport id =
use context = new MyContext()
query {
for airport in context.Airports do
where (airport.Id = id)
select airport
exactlyOne
} |> (fun x -> if box x = null then None else Some x)
let addAirport (entity: Airport) =
use context = new MyContext()
context.Airports.Add(entity) |> ignore
context.SaveChanges true |> ignore
[<EntryPoint>]
let main argv =
let myGuid = Guid.NewGuid()
let airport = {
Id = myGuid
Name = "Michelle"
X = 42.0
Y = 42.0
}
AirportRepository.addAirport airport
let thisAirport = AirportRepository.getAirport myGuid
assert (thisAirport = Some airport)
0

Get the class of nullable type

I am trying to match the type of the nullable String? in a Kotlin reflection exercise:
data class Test(a: String, b: String?)
val test = Test("1", "2")
val properties = test::class.declaredMemberProperties
val propertyNames = properties.joinToString(",") {
when (it.returnType) {
String?::class.createType() -> "string?"
String::class.createType() -> "string"
else -> throw Exception()
}
}
Alas, it is failing with the error, Type in a class literal must not be nullable, for String?::class.
The createType function has an optional nullable parameter that seemed to work when I tested it.
import kotlin.reflect.full.*
String::class.createType(nullable = true) -> "string?"

How to change a member field with Kotlin reflection?

I'm porting a class from Java to Kotlin. This class declares hundreds of objects. Each object has a name property which is identical with the declared variable name of the object. Java reflection allows to use the declared name via reflection to set the object member name. Just saves one parameter in hundreds of constructors.
I try to do the same in Kotlin but can't figure out how to do the property setting. Here is some simplified test code:
import kotlin.reflect.full.companionObject
import kotlin.reflect.full.declaredMemberProperties
class MyTestObject() {
var name: String = "NotInitialized"
companion object {
val Anton = MyTestObject()
val Berta = MyTestObject()
val Caesar = MyTestObject()
}
}
fun main(args : Array<String>) {
println(MyTestObject.Anton.name) // name not yet initialized
// Initialize 'name' with the variable name of the object:
for (member in MyTestObject::class.companionObject!!.declaredMemberProperties) {
if (member.returnType.toString() == "myPackage.MyTestObject") {
println("$member: ${member.name}")
// Set 'name' property to 'member.name':
// ???
}
}
println(MyTestObject.Anton.name) // now with the initialized name
}
The ??? line is where I would like to get access to the name property of MyTestObject to set it to to member.name. I'm looking for a function similar to (member.toObject() as MyTestObject).name = member.name.
While kotlin-reflection strives to be type-safe, sometimes the type system and the inference logic are not enough to allow for the things like what you are trying to do in a type-safe way. So, you have to make unchecked casts, stating that your knowledge about the types is more than the compiler can infer.
In your case, it's enough to cast member so that you can pass the companion object instance into its .get(...) and use the result as a MyTestObject, replace the // ??? line with:
#Suppress("UNCHECKED_CAST")
(member as KProperty1<Any, MyTestObject>)
.get(MyTestObject::class.companionObject!!.objectInstance!!)
.name = member.name
If you can replace MyTestObject::class.companionObject!! with MyTestObject.Companion::class (i.e. your actual use case does not involve getting .companionObject from different classes), the unchecked cast is not needed, and you can replace the statement above with this:
(member.get(MyTestObject.Companion) as MyTestObject).name = member.name
As an alternative that does not require companion object reflection at all, you can do the same binding logic with the delegation. Implementing provideDelegate allows you to customize the logic of initializing the property, and that's where you can assign the names:
operator fun MyTestObject.provideDelegate(
thisRef: MyTestObject.Companion,
property: KProperty<*>
) = apply { name = property.name }
operator fun MyTestObject.getValue(
thisRef: MyTestObject.Companion,
property: KProperty<*>
) = this
Then declare your properties as
val Anton by MyTestObject()
val Berta by MyTestObject()
val Caesar by MyTestObject()
Here is the final test code based on hotkey's solution:
package myPackage
import kotlin.reflect.full.declaredMemberProperties
class MyTestObject() {
lateinit var name: String
companion object {
val Anton = MyTestObject()
val Berta = MyTestObject()
val Caesar = MyTestObject()
init {
for (member in MyTestObject.Companion::class.declaredMemberProperties) {
if (member.returnType.toString() == "myPackage.MyTestObject") {
(member.get(MyTestObject.Companion) as MyTestObject).name = member.name
}
}
}
}
}
fun main(args : Array<String>) {
println(MyTestObject.Anton.name)
println(MyTestObject.Caesar.name)
}

class variables reset back to null in reflection C#

i create an instance of a Person class using reflection and execute its constructor and after that i execute another function of the person class called "Execute":
Assembly assembly = Assembly.GetEntryAssembly();
object personObject = assembly.CreateInstance("ReflectionTest.Person");
// Call Constructor
var ctor = personObject.GetType().GetConstructor(new Type[] { typeof(int) });
var obj = ctor.Invoke(new object[] { 10 });
// Call Method
MethodInfo methodInfo = personObject.GetType().GetMethod("Execute");
object obj1 = methodInfo.Invoke(personObject, null);
Th problem is that all the person class variables i instanciated in the constructor ARE NULL when i call the "Execute" method. why ? and how do i get around this?
In your example, you are invoking the default constructor with this line:
object personObject = assembly.CreateInstance("ReflectionTest.Person");
This would be the proper way to construct the object:
Assembly assembly = Assembly.GetEntryAssembly();
Type personType = assembly.GetType("ReflectionTest.Person");
object inst = Activator.CreateInstance(personType, new object[] { 10 });
I'm not 100%, but could casting obj to a Person help?

Resources