FileHelpers BadUsageException only on compiled F# but not in script - oop

I wrote an fsi script that worked great and wanted to compile it so that I could move it easier. However, when I compiled it, all of a sudden FileHelpers began giving errors.
The following code uses FileHelpers 2.9.9. It is a minimal working example to illustrate the issue, test.fsx:
#r "FileHelpers.dll"
open FileHelpers
[<DelimitedRecord(",")>]
type Type =
val field1 : string
val field2 : int
override x.ToString() = sprintf "%s: %d" x.field1 x.field2
let readFile<'a> file = seq {
use engine1 = new FileHelperAsyncEngine(typeof<'a>)
use tmp1 = engine1.BeginReadFile(file)
engine1.ReadNext() |> ignore
while engine1.LastRecord <> null do
yield engine1.LastRecord :?> 'a
engine1.ReadNext() |> ignore
}
readFile<Type> "test.csv" |> Seq.iter (printfn "%A")
with the file test.csv as
test1,1
test2,2
test3,3
If I run the code as fsi .\test.fsx it will work fine. However, if I try to compile it with fsc .\test.fsx and run .\test.exe I get the error Unhandled Exception: FileHelpers.BadUsageException: The record class Type needs a constructor with no args (public or private). A work around that works in both scripting and compiled mode is
[<DelimitedRecord(",")>]
type Type () =
[<DefaultValue>]
val mutable field1 : string
[<DefaultValue>]
val mutable field2 : int
override x.ToString() = sprintf "%s: %d" x.field1 x.field2
Why would it work as a script but not compiled? I would like to keep it immutable if possible. Thanks for any insight!

FSI uses System.Reflection.Emit to compile your F# code on-the-fly. It appears that types generated with System.Reflection.Emit always have at least one constructor (either the default public constructor or an explicitly defined constructor). Thus it isn't easily possible for the code emitted by FSI to exactly imitate the result of the compiled code, which has no constructors at all (neither public nor private).

Related

mockkStatic for extension method with generic parameter: "Not enough information to infer type variable T"

FYI: I'm currently using Mockk-1.12.4 and Kotlin-1.6
I've got an extension method that returns an object of type T:
fun <T> Entity.selectReferenceAsSingleObject(referenceName: String): T {
return this.selectReferencesByName(referenceName).single().asObjet() as T
}
This is defined as top level function in an Extentions.kt file (so not a class). I'm wanting to mock this with MockK by using mockkStatic
mockkStatic(Entity::selectReferenceAsOptionalSingleObject)
However I'm getting the error:
Not enough information to infer type variable T
because it cannot work out what type I should be mocking. I've tried adding my type parameter in a bunch of places
mockkStatic<KFunction<MyType>>(Entity::selectReferenceAsSingleObject)
mockkStatic<MyType>(Entity::selectReferenceAsSingleObject)
mockkStatic(Entity<KFunction<MyType>>::selectReferenceAsSingleObject)
mockkStatic(Entity<MyType>::selectReferenceAsSingleObject)
mockkStatic(Entity::<KFunction<MyType>>selectReferenceAsSingleObject)
mockkStatic(Entity::<MyType>selectReferenceAsSingleObject)
mockkStatic(Entity::selectReferenceAsSingleObject<KFunction<MyType>>)
mockkStatic(Entity::selectReferenceAsSingleObject<MyType>)
mockkStatic(Entity::selectReferenceAsSingleObject as KFunction<MyType>)
mockkStatic(Entity::selectReferenceAsSingleObject as MyType)
But nothing works and it either tells me the same error or tells me that type arguments are not allowed there. I'm not sure what I need to do to fix my syntax as IntelliJ isn't giving me any hints so I feel kind of stuck. Any help would be appreciated.
Try this:
mockkStatic("your.package.YourFileExtensionKt")
you can also give the file a nice name:
#file:JvmName("Extension")
would look like this:
mockkStatic("your.package.Extension")
anyway for the test to work you must use a mock in the extended class, in this case Entity, it would look like this:
#Test
fun test() {
val entity : Entity = mockk(relaxed = true)
every { entity.selectReferenceAsSingleObject<Any>(any()) } returns "whatever"
val result = entity.selectReferenceAsSingleObject<Any>("test")
assertEquals("whatever", result)
}

Ununderstood type variance using kotlin variance

I'm using generics to not reuse code and I' running into a lack of understanding for type generics. I have a class Writer (java code from another library).
public class Writer<T>
A class FileWriter (java code from another library)
public class FileWriter<D>{
FileWriter(Writer<D> writer){
this.writer=writer
}
public void append(D datum){
//Does something
}
}
Now I'm initiating this in kotlin:
val writer = FileWriter(Writer(AGenratedJavaClassThatIMplementsSpecificRecord::class.java))
I can now call writer.append with AGenratedJavaClassThatIMplementsSpecificRecord(). It works just fine
I would like to pass this writer to a function.
funDoSomethingExtra(writer: FileWriter<in SpecificRecord>)
This gives me an error that I do not understand.
Type mismatch: inferred type is FileWriter<AGenratedJavaClassThatIMplementsSpecificRecord!>! but FileWriter<in SpecificRecord> was expected
Changing this to
funDoSomethingExtra(writer: FileWriter<out SpecificRecord>)
Makes writers.append give the error
Required Nothing, found AGenratedJavaClassThatIMplementsSpecificRecord.
Without the use of methods, all works fine. Which details Am I missing? It is probably something small,
Kind regards,
Jelmew
This line of your code:
val writer = FileWriter(Writer(AGenratedJavaClassThatIMplementsSpecificRecord::class.java))
does not specify the type of the FileWriter, so it is inferred from your arguments to the constructors, so the type of writer is FileWriter<AGenratedJavaClassThatIMplementsSpecificRecord>.
Your signature funDoSomethingExtra(writer: FileWriter<in SpecificRecord>) is correct for calling a method on the writer that does something to a SpecificRecord or subtype of SpecificRecord. However, your
FileWriter<AGenratedJavaClassThatIMplementsSpecificRecord>
cannot be cast to a FileWriter<in SpecificRecord> because AGenratedJavaClassThatIMplementsSpecificRecord is a subtype of SpecificRecord, not a supertype. The compiler knows your file writer can consume AGenratedJavaClassThatIMplementsSpecificRecord, but it doesn't know it can consume the less-specific type SpecificRecord. There's the possibility of you calling some function of the subtype that doesn't exist in the supertype.
So to be able to pass your writer to this function, it needs to be a FileWriter<SpecificRecord> or FileWriter<in SpecificRecord>. You can't safely cast it after its type is already assigned, but you can assign it the proper type right at the declaration site instead of letting the compiler try to infer it:
val writer: FileWriter<SpecificRecord> = FileWriter(Writer(AGenratedJavaClassThatIMplementsSpecificRecord::class.java))

How to get Class<java.lang.Long> in Kotlin?

Writing some querydsl code. In Java I would do like this:
#Test
void countTest() {
NumberPath<Long> cnt = Expressions.numberPath(Long.class, "count");
NumberPath<Long> typeId = Expressions.numberPath(Long.class, "type_id");
List<Long> fetched = sql.select(typeId)
.from(SQLExpressions.select(tGroup.typeId.as(typeId), tGroup.count().as(cnt))
.from(tGroup)
.groupBy(tGroup.typeId))
.where(cnt.gt(100L)).fetch();
System.out.println(fetched);
}
Notice this Long.class in Expressions.numberPath(Long.class, ...)
If I create a .kt file and copy-paste the above Java code, it will be converted by Intellij to:
Expressions.numberPath(Long::class.java, ...).
So the resulting Kotlin code I have is:
val cnt = Expressions.numberPath(Long::class.java, "count")
val typeId = Expressions.numberPath(Long::class.java, "type_id")
val fetched = sql.select(typeId)
.from(SQLExpressions.select(QTGroup.tGroup.typeId.`as`(typeId), QTGroup.tGroup.count().`as`(cnt))
.from(QTGroup.tGroup)
.groupBy(QTGroup.tGroup.typeId))
.where(cnt.gt(100L)).fetch()
println(fetched)
Now when I run the code, I get:
java.lang.IllegalArgumentException: Unsupported target type : long
at com.querydsl.core.util.MathUtils.cast(MathUtils.java:86)
at com.querydsl.core.types.dsl.NumberExpression.cast(NumberExpression.java:178)
at com.querydsl.core.types.dsl.NumberExpression.gt(NumberExpression.java:337)
at project.dao.QuerydslKotlinCountTest.countTest(QuerydslKotlinCountTest.kt:30)
So it's not a Class<java.lang.Long> which I would expect, but some class Class<long> (never seen this before and can not get it programmatically with Class.forName("long")).
So, how do I make this simple piece of code work in Kotlin?
If I replace Long::class.java with java.lang.Long::class.java, the code does not compile:
Error:(27, 104) Kotlin: None of the following functions can be called with the arguments supplied:
public open fun `as`(p0: Path<Long!>!): NumberExpression<Long!>! defined in com.querydsl.core.types.dsl.NumberExpression
public open fun `as`(p0: String!): NumberExpression<Long!>! defined in com.querydsl.core.types.dsl.NumberExpression
The only way I made it work is using a boxed java primitive: java.lang.Long.valueOf(1).javaClass, but it looks ugly.
Try using KClass's javaObjectType property instead of java, e.g.:
1L::class.java // returns long
1L::class.javaObjectType // returns java.lang.Long
From its documentation:
[...] In case of primitive types it returns corresponding wrapper classes.

Kotlin extension properties do not work in Kotlin script

var StringBuilder.lastChar: Char
get() = get(length - 1)
set(value: Char) {
this.setCharAt(length - 1, value)
}
val sb = StringBuilder("Kotlin?")
sb.lastChar = '!'
println(sb)
When I run this code with kotlinc -script, the compiler throws:
extensions.kts:3:14: error: unexpected type specification
set(value: Char) {
However, this code works without problems when compiled.
So, does it mean extension properties cannot work in kotlin script mode?
Kotlin scripts just wrap code in main function. And extension properties doesn't work when defined inside functions. I think this is bug, and you can submit it here: https://youtrack.jetbrains.com/issues/KT.

How can I hide methods in F#?

I am currently implementing a Spec framework in F# and I want to hide the Equals, GetHashCode etc. methods on my should type, so that the API is not cluttered with these.
I know in C# it is done by making the class implement an interface like this:
using System;
using System.ComponentModel;
public interface IFluentInterface
{
[EditorBrowsable(EditorBrowsableState.Never)]
bool Equals(object other);
[EditorBrowsable(EditorBrowsableState.Never)]
string ToString();
[EditorBrowsable(EditorBrowsableState.Never)]
int GetHashCode();
[EditorBrowsable(EditorBrowsableState.Never)]
Type GetType();
}
I tried doing the same in F#:
type IFluentInterface = interface
[<EditorBrowsable(EditorBrowsableState.Never)>]
abstract Equals : (obj) -> bool
[<EditorBrowsable(EditorBrowsableState.Never)>]
abstract ToString: unit -> string
[<EditorBrowsable(EditorBrowsableState.Never)>]
abstract GetHashCode: unit -> int
[<EditorBrowsable(EditorBrowsableState.Never)>]
abstract GetType : unit -> Type
end
Implemented it in my type:
interface IFluentInterface with
member x.Equals(other) = x.Equals(other)
member x.ToString() = x.ToString()
member x.GetHashCode() = x.GetHashCode()
member x.GetType() = x.GetType()
but without success.
I also tried to override the methods in my type and adding the attribute that way, but that didn't do the trick either.
So the question remains, how can I clean up my API ?
Edit:
Thanks to the help (see below) I was able to solve my problem.
In summary, .Equals and .GetHashCode can be hidden via [<NoEquality>] [<NoComparison>] but that will also change the semantics.
The hiding via EditorBrowsable attributes does not work.
The only way to have a clean API and still be able to overload methods is to make these method members static.
The resulting class can be found by browsing inside my project FSharpSpec.
The type in question can be found here.
Thanks to everyone who helped me solve this problem.
Cheers ...
Alternatively, you could design the library using an alternative style using functions enclosed in a module. This is the usual way for writing functional code in F# and then you won't need to hide any standard .NET methods. To complete the example given by 'kvb', here is an example of object-oriented solution:
type MyNum(n:int) =
member x.Add(m) = MyNum(n+m)
member x.Mul(m) = MyNum(n*m)
let n = new MyNum(1)
n.Add(2).Mul(10) // 'ToString' shows in the IntelliSense
The functional way of writing the code might look like this:
type Num = Num of int
module MyNum =
let create n = Num n
let add m (Num n) = Num (m + n)
let mul m (Num n) = Num (m * n)
MyNum.create 1 |> MyNum.add 2 |> MyNum.mul 10
If you type MyNum., the F# IntelliSense will show the functions defined in the module, so you won't see any noise in this case.
Repeating my answer from
http://cs.hubfs.net/forums/thread/13317.aspx
In F# you can disallow Equals & GetHashCode (and remove them from intellisense) by annotating the type with the NoEquality and NoComparison attributes, as shown below. User-defined methods can also be hidden from the intellisense list via the Obsolete attribute or the CompilerMessage attribute with IsHidden=true. There is no way to hide the System.Object methods GetType and ToString from the F# intellisense.
[<NoEquality; NoComparison>]
type Foo() =
member x.Bar() = ()
member x.Qux() = ()
[<System.Obsolete>]
member x.HideMe() = ()
[<CompilerMessage("A warning message",99999,IsError=false,IsHidden=true)>]
member x.WarnMe() = ()
let foo = new Foo()
foo. // see intellisense here
I don't think that there is any way to do that in F# in general. In the particular case of .Equals and .GetHashCode, you can make them unusable by putting a [<NoEquality>] attribute on your type, but this actually has a semantic effect in addition to hiding those methods.
EDIT
It might also be worth mentioning that fluent interfaces are rarely used in F#, since it's much more idiomatic to use combinators and pipelining instead. For instance, imagine that we want to create a way to create arithmetic expressions. Rather than
let x = Expressions.MakeExpr(1).Add(2).Mul(3).Add(4)
I think that most F# users would prefer to write
open Expressions
let x =
1
|> makeExpr
|> add 2
|> mul 3
|> add 4
With this style, there's no need to hide members because expressions get piped to combinators, rather than calling methods of an expression builder.