34
loading...
This website collects cookies to deliver better user experience
public static T Crd<T>(this T value) where T : IFloatingPoint<T> {
T two = T.One + T.One;
return two * T.Sin(value / two);
}
let inline crd< ^t when ^t :> IFloatingPoint< ^t>> (value:^t) =
value.Crd()
^t
is. An in doing so, this could be considered exactly identical to the C# signature. There's a method, a generic type constrained to a CRTP interface, a single parameter of that generic type, and a return of that type. If you have a single generic method that needs to be bound to, this is the least effort. Easy enough.let inline ( + ) (left:^a) (right:^b):^c =
((^a or ^b or ^c) :
(static member op_Addition : ^a -> ^b -> ^c)(left, right))
(^a or ^b or ^c)
says the member constraint we're looking for can be found on any of the types: lefthand, righthand, or return. This forms a critical part of covering both preexisting types, and extending seamlessly to new types. (static member op_Addition
tells the compiler we're binding to a static member, which all operators are, called op_Addition
, which is the special name the compiled IL uses, that CLS Compliant languages understand as the +
operator. ^a -> ^b -> ^c
is, of course, the signature of the operator. By using different type definitions we can potentially have unique types in different parameters, but, of course, can all be unified to the same type like the standard F# operators. This yields more flexibility than the standard F# definition, which, yay.Contains()
which exists only for Span<T>
and ReadOnlySpan<T>
. Arguments can definitely be made about Memory<T>
, how it should be conceptualized, what it should support, but arrays? Really? No one thought to include a method to check arrays for a value? Sure, the loop isn't hard to write, except that more efficient checks which use partial loop unrolling or vectorization are actually non-trivial, and clutter code. Implementing this was simple enough:public static Boolean Contains<T>(this T[]? collection, T element)
where T : IEquatable<T> =>
MemoryExtensions.Contains(collection.AsSpan(), element);
Span<T>
and ReadOnlySpan<T>
. I wrote before about binding to multiple overloads when they're all in a single static class, so I won't cover that again. But as for multiple? It's doable, but it's a closed extension.let inline Contains< ^t, ^u, ^a, ^b when (^t or ^u or ^a or ^b) :
(static member Contains : ^a -> ^b -> Boolean)>
collection element =
((^t or ^u or ^a or ^b) :
(static member Contains : ^a -> ^b -> Boolean)
(collection, element))
^u
. The function signature looks like this:let inline contains (collection:^a) (element:^b) =
Contains<CollectathonExtensions, MemoryExtensions, ^a, ^b>
collection element
System.Collections.Generic
and System.Collections.Immutable
. Say we add a Requeue
method for both respective Queue
types. This isn't an easy problem to solve, and one or the other will collide and cause problems. Of course, standard extension method calls are viable.type pattern = Pattern
Pattern
has many predefined patterns, especially for UNICODE Categories or Contributory Properties. Luckily, these are also very straightforward.let letter = Pattern.Letter
let inline blockComment (start:^a) (stop:^b) =
BlockComment<Pattern, ^a, ^b> start stop
let inline fuzzy (pattern:^a):pattern =
Fuzzy<Pattern, ^a> pattern
let inline fuzzy2 (maxEdits:int) (pattern:^a):pattern =
Fuzzy2<Pattern, ^a> pattern maxEdits
2
as a suffix is a well understood convention, so downstream will generally understand it. Preferably however, use a clarifying suffix.let inline many (pattern:^a):pattern =
Many<StringierPatternsExtensions, ^a> pattern
Pattern
has constructors, but .NET doesn't like constraining on parameterized constructors. Well, F# doesn't care too much.let inline ptn (pattern:^a):pattern =
(^t : (new : ^a -> Pattern) pattern)
p
, taking after the FParsec pchar
and pstring
conventions, but I've decided more recently that's too vague. ptn
seems to strike a good ballance between succinct and clear.<|>
, it's own operator. Most just assume you're going to use the C# API. You can use ||
or |||
however, without creating issues, as long as you put it together with a few things we've covered earlier.let inline ( || ) (left) (right) =
Or<StringierPatternsExtensions, NumbersomeExtensions, _, _, _>
left right
public static Boolean Or(this Boolean left, Boolean right) =>
left || right;
public static T Or<T>(this T left, T right)
where T : IBitwiseOperators<T, T, T> =>
left | right;
let parse (source:^a) (location:byref<int>) (pattern:pattern):^b option
source
and location
into a record or something, it could be entirely idiomatic F#. Since the signature is so different, you run into issues, but I really do want to make this work. What I do know, is that a technique I've never covered here is useful. Sometimes you might not want to bind to the public API, but rather, recompose internal APIs into the same effect. Enter: InternalsVisibleToAttribute
. When this is applied, it allows other assemblies to still access internal members.let inline parse (source:^a) (location:byref<int>)
(pattern:pattern):^b option =
let start:int = location
match pattern.Head.Consume(
source, &location, Unchecked.defaultof<StringComparison>) with
| null -> Some(source[start..location])
| _ -> None
^a
to ReadOnlySpan<Char>
. I think I can write an overloader for Consume
and have this work, although the additional slicing has me unsure. If I manually declare ^a
and ^b
as String
, it does work.