Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Using linear and exponential ranges from C# #156

Closed
Porges opened this issue Feb 16, 2018 · 20 comments
Closed

Using linear and exponential ranges from C# #156

Porges opened this issue Feb 16, 2018 · 20 comments
Labels

Comments

@Porges
Copy link
Contributor

Porges commented Feb 16, 2018

When using Range.Linear I got this:

System.NotSupportedException: 'Specified method is not supported.'
   at Hedgehog.Range.Linear@155.Invoke(Int32 sz)
   at Hedgehog.Gen.List@338-1.Invoke(Seed seed, Int32 size)
   at Hedgehog.Random.bind@45.Invoke(Seed seed, Int32 size)
   at Hedgehog.Random.map@39-2.Invoke(Seed seed, Int32 size)
   at Hedgehog.Gen.bindRandom@40.Invoke(Seed seed0, Int32 size)
   at Hedgehog.Random.map@39-2.Invoke(Seed seed, Int32 size)
   at Hedgehog.Random.map@39-2.Invoke(Seed seed, Int32 size)
   at Hedgehog.Gen.bindRandom@40.Invoke(Seed seed0, Int32 size)
   at Hedgehog.Property.loop@332-21(Int32 n, Random`1 random, Seed seed, Int32 size, Int32 tests, Int32 discards)
   at Hedgehog.Property.Check(Property`1 g)

Range.Constant works fine, so I'm sticking with that at the moment. I can't see any obvious NotSupportedException uses in the code around line 155...

@moodmosaic
Copy link
Member

That's weird... We're using Range.linear in many places, like here. 😕

@moodmosaic
Copy link
Member

Is this happening when you use Hedgehog from C#?

@Porges
Copy link
Contributor Author

Porges commented Jan 31, 2019

Yes, it was.

@moodmosaic
Copy link
Member

The issue seems to be happening in here

ToBigInt.Invoke x

(Called from here)

let inline scaleLinear (sz0 : Size) (z0 : 'a) (n0 : 'a) : 'a =
let sz =
max 0 (min 99 sz0)
let z =
toBigInt z0


On my local, I get a more specific exception actually:

System.NotSupportedException : Dynamic invocation of ToBigInt is not supported

Stack Trace:

\fsharp-hedgehog\src\Hedgehog\Range.fs(155,0):
  at Hedgehog.Range.Linear@155.Invoke(Int32 sz)

\fsharp-hedgehog\src\Hedgehog\Range.fs(42,0):
  at Hedgehog.Range.Bounds[a](Int32 sz, Range`1 _arg1)

\fsharp-hedgehog\src\Hedgehog\Gen.fs(458,0):
  at Hedgehog.Gen.Int32@458-3.Invoke(Seed seed, Int32 size)

[...]

@moodmosaic moodmosaic added the bug label Feb 19, 2019
@moodmosaic moodmosaic changed the title Strange NotSupportedException Using linear and exponential ranges from C# Feb 19, 2019
@moodmosaic
Copy link
Member

Seems to be specific to C#, but haven't tried with VB.NET...

@Porges
Copy link
Contributor Author

Porges commented Feb 19, 2019

For performance reasons it's probably better to not go via BigInt as well, we could change the generators perhaps...

@moodmosaic
Copy link
Member

Sure, but what the alternatives are?

@Porges
Copy link
Contributor Author

Porges commented Feb 19, 2019

I mean for the specific types like Int32 and so on we could generate them directly and not generate a BigInt first. We'd still need the BigInt version for generic numerics.

@mausch
Copy link
Contributor

mausch commented Oct 2, 2020

I just got this exception, and when I googled it I landed on this github issue and found I had already upvoted it 😄

I'm sure you know all this by now, but just to document it explicitly here:
This happens because these functions have statically resolved type parameters... and as everyone knows that kills interop with any other .net langs.

For example linearBounded gets compiled to this:

    [CompilationSourceName("linearBounded")]
    public static Range<a> LinearBounded<a>()
    {
      if (true)
        throw new NotSupportedException("Dynamic invocation of MinValue is not supported");
      a lo = (a) null;
      if (true)
        throw new NotSupportedException("Dynamic invocation of MaxValue is not supported");
      a hi = (a) null;
      a zero = LanguagePrimitives.GenericZeroDynamic<a>();
      return Range<a>.NewRange(new System.Tuple<a, FSharpFunc<int, System.Tuple<a, a>>>(zero, (FSharpFunc<int, System.Tuple<a, a>>) new Range.LinearBounded\u0040166<a>(lo, hi, zero)));
    }

(plus some metadata for F# consumers, I've not used this in quite some time so I forget but it doesn't matter anyway here)

The workaround is to just not call such functions from C# 🙂
For example if the C# programmer using Hedgehog is willing to add an F# project to their solution, they can just explicitly instantiate things for the concrete types they need e.g.:

module HedgehogWorkaround =
    let linearBoundedInt : Range<int> = linearBounded()

and then call that instead in their C# code and yes it's cumbersome but things will just work.

I'd suggest mangling the compiled name of such functions so that C# callers don't accidentally call them.

@moodmosaic
Copy link
Member

Mauricio (@mausch), thank you for commenting and for the analysis 👍

The workaround is to just not call such functions from C# [..] I'd suggest mangling the compiled name of such functions so that C# callers don't accidentally call them.

Couldn't we just remove the [<CompiledName("LinearBounded")>] annotation?

For example if the C# programmer using Hedgehog is willing to add an F# project to their solution, they can just explicitly instantiate things for the concrete types they need e.g.:

module HedgehogWorkaround =
   let linearBoundedInt : Range<int> = linearBounded()

Perhaps... we can add those in a module and include it with the library? 🤔

@mausch
Copy link
Contributor

mausch commented Oct 3, 2020

Hi Nikos,

Couldn't we just remove the [<CompiledName("LinearBounded")>] annotation?

Unfortunately this will only expose the function to C# callers as linearBounded.

Perhaps... we can add those in a module and include it with the library?

Sure, it could be done ideally, my PR was just about preventing the exception :-)

@mausch
Copy link
Contributor

mausch commented Oct 3, 2020

Perhaps... we can add those in a module and include it with the library?

Something like this should do : https://github.com/mausch/fsharp-hedgehog/commit/29b29b52dc1d86dfff498fd284ed9e6413c1e3ac
Or you could put it in a separate module if you want, up to you.
And of course it would have to be done for every similar static inline function.

@moodmosaic
Copy link
Member

👍 It looks like we need both, then. PR #219, and another one based on https://github.com/mausch/fsharp-hedgehog/commit/29b29b52dc1d86dfff498fd284ed9e6413c1e3ac.

@mausch
Copy link
Contributor

mausch commented Oct 3, 2020

So now that #219 is merged, I started the tedious process of copypasting all the overloads for all these static inline functions ( see https://github.com/mausch/fsharp-hedgehog/commit/d5ac038d1f370fce2b449b4bc1f18272e2360cbc ).
However these functions prefixed with __ don't get pushed to the end of the autocomplete list as I expected... at least in Rider.
Either we accept this or we write a separate interface for C# callers for Range / Gen...
Any other ideas? @Porges ?

@moodmosaic
Copy link
Member

@mausch, in Visual Studio Community 2019 (16.7.5) this appears to work

[<EditorBrowsable(EditorBrowsableState.Never)>]

I've sent you https://github.com/mausch/fsharp-hedgehog/pull/1/files — curious to see if it works in Rider and/or Ionide. 👀

@moodmosaic
Copy link
Member

Duh! That defeats the whole purpose and they're now also hidden globally 😥

@moodmosaic
Copy link
Member

However these functions prefixed with __ don't get pushed to the end of the autocomplete list as I expected... at least in Rider.

FWIW, in Visual Studio Community 2019 (16.7.5) they are pushed to the end of the autocomplete list.

@mausch
Copy link
Contributor

mausch commented Oct 4, 2020

Rider has an option to sort the autocomplete list by name, and then these __ functions get out of the way. It's just not the default.
I've also tried other prefixes like ~~ but they made no difference.
Anyway this isn't something we should be even trying to control I don't think.

I think #222 is a reasonable workaround for now, even if it's obviously not ideal.
Similar issues like #171 and #172 show that ultimately there should be a completely separately interface for C#... if anyone is willing to put in the time to do it of course 🙂

Many years ago I attacked these F# -> C# interop issues with some helper functions to convert e.g. FSharpFunc <-> Func , and while that does make things usable most of the time, of course it can't be as good as an interface specifically designed for C# .

@ghost
Copy link

ghost commented Jan 12, 2021

@Porges @moodmosaic I think this is possible to do now, should we consider this done?

@moodmosaic
Copy link
Member

Yes, we should. 👍

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

3 participants