Ranter
Join devRant
Do all the things like
++ or -- rants, post your own rants, comment on others' rants and build your customized dev avatar
Sign Up
Pipeless API
From the creators of devRant, Pipeless lets you power real-time personalized recommendations and activity feeds using a simple API
Learn More
Comments
-
@SortOfTested haven't tried yet (since at home and work machine at office), but... I just would really like to know why Microsoft.Azure.ServiceBus.SubscriptionClient.RegisterMessageHandler(Func<Message,CancellationToken,Task>, MessageHandlerOptions) gets the function signature Message -> CancellationToken -> Task in F#, but Microsoft.ServiceBus.Messaging.SubscriptionClient.OnMessageAsync(Func<BrokeredMessage,Task>, OnMessageOptions) keeps the bloody Func<BrokeredMessage,Task> signature and insist on adding the -> Task at the end? It just doesn't really make sense to me...
For the record, I was using Microsoft.Azure.ServiceBus first, but due to restrictions imposed upon me by the bloody enterprise in-house libs I needed to change for Microsoft.ServiceBus.Messaging, as the bleeding muthafucka needs the BrokeredMessage while Message is no good. Argh.
... basically only because I need the (Brokered)Message class extension method GetBody<T>() w/o XmlSerializer... -
@100110111
Edit: on revision, it makes sense
The second doesn't require a discretionary bifunctor, so that may be why. Generally point free is valuable when you have an arity 2 or greater. Func<T,R> technically only has arity 1, even thought it has typographical arity 2. Another way to say it is there's little point in composing unary function arguments. -
@SortOfTested while I kind of understand what you are saying, somehow I don't get it. Would you mind opening it up for me a bit more? 😅
-
Yeah, no doubt, I love this stuff.
Consider the Func arg signature on Microsoft.Azure.ServiceBus.SubscriptionClient.RegisterMessageHandler:
Func<Message,CancellationToken,Task>
As you know, that means it's going to accept any "2-ary" (bifunctor) method or function that satisfies the (Message, CancellationToken) -> Task signature.
(message, token) => Task;
In the point-free style in F# we get:
message -> token -> Task;
This gives us some cool capabilities like partial argument binding ( strategy-composition, currying), ex:
let bifunctor = fun x y -> x + y
let partialBifunctor = bifunctor 1
let result = partialBifunctor 2 // expect 3 -
@100110111
Now consider the second signature:
Func<BrokeredMessage,Task>
F# fn expression would be:
fun message -> sendMessage(message);
This is a "1-ary" unary functor signature. Since it only takes a single argument, the function executes when the last arugment is satisfied, and is therefore mechanically equivalent to Func<T,R>, ex:
let unaryFunctor = fun x -> x * 2;
let unaryResult = unaryFunctor 2; // expect 4
Given that, there's no value in coercing the signature.
A liftable binary signature would be something like:
Message -> (unit -> Task)
Obligatory ex:
let unaryFunctorComposable = fun x -> fun() -> x * 2;
let unaryUnitComposition = unaryFunctorComposable 2; // (unit -> int)
let unaryUnitResult = unaryUnitComposition(); // expect 4 -
@SortOfTested I wish I had you explaining things to me every time I don't seem to quite get the hang of something! Much better than anything my google-fu yielded. Thanks a lot.
-
@100110111
Any time. My F# is a bit rusty, but my functionals are strong. It actually took me a hot minute of "will this work in F# like I expect?" to get to the answer, hence the original edit.
I see signatures of x y => x*y like x -> y -> z, and know there's some degree of eta-expansion in the language. So then I'd try something like this expecting it to work as is:
let stringFn () = "Test"
let printString (str: string) = printf "%s" str
printString stringFn
And of course it doesn't bc:
unit -> string is not type string.
No biggie, just means I have to manually handle the conversion of unit -> string to string.
let stringFn () = "Test"
let printString (str: string) = printf "%s" str
printString (stringFn())
Languages like Scala take an odd approach that:
fn = x -> y -> x * y
is really just sugar for
def _fn(x,y) -> x*y // shorthand
fn = (x) -> _fn.apply(x) -> (y) -> fn.apply(y)();
Which allows it to perform implicit eta reduction on units that result in the correct type.
Annnnnyways >.<
One of the many good things about F# is that it seamlessly integrates with the .NET ecosystem, right? Very handy in an enterprise environment where in order to get anything done you have to use in-house nugets and tediously building a C# app for something you can do in about 30 LoC in F# just doesn't make sense...
... And then you run into the one fucking namespace in the whole ecosystem that just DOES. NOT. WORK. with F#. What the actual fuck M$?!
In all other cases Func<T',Task> in C# translates into T' -> Task in F#, but not here. "Oh, you're trying to give me Func<T',Task> -> Task? Can't do". Fuck that.
rant