For no other reason than it looks ugly and is hard to explain for non-OCaml programmers, is there an alternative to
let* _ = do_something in
?
There is of course >>= fun() ->, which does not look better.
Maybe
let (;*) = >>= fun () ->
?
First of all, it is better to write,
let* () = do_something in
...
To make it explicit that the return type is unit.
The is no provision for a monadic operation returning unit in the new binding operator syntax, but there exist various ppx extensions, e.g., ppx_monadic that provide the do-notation.
Finally, you can use sequencing operators, provided by your monadic library, for example, in Monads there is sequence, so you can write,
sequence [
do_one_thing;
do_another_thing;
and_so_on;
]
Related
std::transform from the <algorithm> header applies to ranges, and it is what "enables" us to use ranges as the functors they are (in the sense of category theory(¹)). std::transform is iterator-based, yes, but std::ranges::views::transform is not, and its signature closely matches the signature of corresponding functions in functional languages (modulo the different order of the two arguments, but this is not a big deal).
When I saw this question (and in the process of answering to it), I learned that C++23 introduces std::optional<T>::transform, which makes std::optional a functor too.
All these news truly excite me, but I can't help thinking that functors are general, and that it'd be nice to have a uniform interface to transform any functor, as is the case in Haskell, for instance.
This makes me think that an object similar to std::ranges::views::transform (with a different name not alluding to ranges) could be made a customization point that the STL would customize not just for ranges, but also for std::optional and for any other functor in the STL, whereas the programmer could customize it for their user-defined classes.
Quite similarly, C++23 also introduces std::optional<T>::and_then, which is basically the monadic binding for std::optional. I'm not aware of any similar function that implements monadic binding for ranges, but C++20's some_range | std::ranges::views::transform(f) | std::ranges::views::join is essentially the monadic binding of some_range with f.
And this makes me think that there could be some generic interface, name it mbind, that one can opt in with any type. The STL would opt in for std::optional by implementing it in terms of std::optional<T>::and_then, for instance.
Is there any chance, or are there any plans that the language will one day support such a genericity?
I can certainly see some problems. Today std::ranges::views::transform(some_optional, some_func) is invalid, so some code might be relying on that via SFINAE. Making it suddenly work would break the code.
(¹) As regards the word functor, I refer to the definition that is given in category theory (see also this), not to the concept of "object of a class which has operator() defined"; the latter is not defined anywhere in the standard and is not even mentioned on cppreference, which instead uses the term FunctionObject to refer to
an object that can be used on the left of the function call operator
I'm not aware of any similar function that implements monadic binding for ranges, but C++20's some_range | std::ranges::views::transform(f) | std::ranges::views::join is essentially the monadic binding of some_range with f.
ranges::views::for_each is monadic bind for ranges (read), although it is just views::transform | views::join under the hood.
As for whether you'll get a generic interface for Functor and Monad. I doubt it unless such genericity will be useful to library writers writing templates. std::expiremental::future is monadic also (and I imagine Executors are too), so one could to write generic algorithms such as foldM over these three types. I think Erik Niebler has shown with range-v3 that it is possible to write a Functor/Monad library at the expense of hand coding every pipe operator i.e.
#include <fp_extremist.hpp>
template <typename M> requires Monad<M>
auto func(M m)
{
return m
| fp::extremist::fmap([](auto a) { return ...; })
| fp::extremist::mbind([](auto b) { return ...; })
;
}
What I think is actually possible, is that we'll get UFCS and a |> operator so we can get the benefits of invocable |> east syntax and the ability to pipe to algorithms. From Barry's blog:
It doesn’t work because while the range adapters are pipeable, the algorithms are not. ...
That is, x |> f still evaluates as f(x) as before… but x |> f(y) evaluates as f(x, y).
P.S. it's not hard to give the definition of a Functor in c++: <typename T> struct that provides transform.
P.S.S
Edit: I realised how to handle applicatives.
Applicative<int> a, b, c;
auto out = a
| zip_transform(b, c
, [](int a, int b, int c){ return a + b + c; })
;
zip_transform because it zips a, b, c into a Applicative<std::tuple<int, int, int>> and then transforms it (think optional, future, range). Although you could always work with Applicatives of partially applied functions, but that would involve lots of nested functions which is not the style of c++ and would disrupt the top to bottom reading order.
And this makes me think that there could be some generic interface, name it mbind, that one can opt in with any type. ...
Is there any chance, or are there any plans that the language will one
day support such a genericity?
There is P0650 which proposes a non-member monadic interface, customizable using traits. The paper shows customizations for expected and other types, implementable in C++17.
The accepted proposal for std::optional monadic operations P0798 §13.1 references P0650 while discussing alternatives to member-function syntax:
Unfortunately doing the kind of composition described above would be very verbose with the current proposal without some kind of Haskell-style do notation
std::optional<int> get_cute_cat(const image& img) {
return functor::map(
functor::map(
monad::bind(
monad::bind(crop_to_cat(img),
add_bow_tie),
make_eyes_sparkle),
make_smaller),
add_rainbow);
}
My proposal is not necessarily an alternative to [P0650]; compatibility between the two could be ensured and the generic proposal could use my proposal as part of its implementation.
It goes on to mention how other C++ features still in development, like unified call syntax, might provide a more concise syntax for generic monadic operations.
The accepted proposal for std::expected monadic operations P2505 doesn't reference P0650 directly, but discusses "Free vs member functions" as part of its design considerations, ultimately prioritizing consistency with the std::optional monadic interface.
I'm new to OCaml, but have worked with Rust, Haskell, etc, and was very surprised when I was trying to implement bind on Either, and it doesn't appear that any of the general implementations have bind implemented.
JaneStreet's Base is missing it
What I assume is the standard library is missing it
bind was the first function I reached for... even before match, and the implementation seems quite easy:
let bind_either (m: ('e, 'a) Either.t) (f: 'a -> ('e, 'b) Either.t): ('e, 'b) Either.t =
match m with
| Right r -> f r
| Left l -> Left l
Am I missing something?
It is because we prefer a more specific Result.t, which has clear names for the ok state and for the exceptional state. And, in general, Either.t is not extremely popular amongst OCaml programmers as usually, a more specialized type could be used with the variant names that better communicate the domain-specific purpose of either branch. It is also worth mentioning that Either was introduced to the OCaml standard very recently, just 4.12, so it might become more popular.
As mentioned by #ivg, Either is relatively new to the standard library and generally one would prefer to use types that make more sense. For example, Result for error handling.
There is also another point of view, which also applies to Result. Monads act on types parameterised by one type.
In Haskell, this is much less obvious because it is possible to partially apply type constructors. Hence; bind:: (a -> b) -> Either a -> Either b allows you to go from Either a c to Either b c.
In trying to generalise the behaviour of a monad via parameterised modules (functors in the ML sense of the term), one would have to "trick" oneself into standardising, for example, the treatment of option (a type of arity 1) and either (or result) which are of arity 2.
There are several approaches. For example, expressing multiple interfaces to describe a monad. For example describing Monad2 and describing Monad in terms of Monad2 as is done in the Base library (https://ocaml.janestreet.com/ocaml-core/latest/doc/base/Base/Monad/index.html)
In Preface we used a rather different (and perhaps less generic) approach. We leave it to the user to set the left parameter of Either (via a functor) (and the right parameter for Result): https://github.com/xvw/preface/blob/master/lib/preface_stdlib/either.mli
However, we do not lose the ability to change the left-hand type of the calculation because Either also has a Bifunctor module that allows us to change the type of both parameters. The conversation is broadly described in this thread: https://discuss.ocaml.org/t/instance-modules-for-more-parametrized-types/5356/2
For this code:
// val map : ('a -> 'b [#bs]) -> 'a option -> 'b option
let optTest: Js.Option.t(int) = Js.Option.map(x => x, Js.Option.some(1));
I am getting following error:
This expression should not be a function, the expected type is (. 'a) => 'b
where x => x is red. I am really confused, why the map isn't working? From its type signature it looks I am using it correctly, yet compiler says the first argument is not supposed to be a function?
Short answer - use Belt.Option.map instead:
let optTest: option(int) = Belt.Option.map(Some(1), x => x);
Long answer:
The Js namespace is intended mostly for bindings to JavaScript's standard APIs. And while Js.Option for historical reasons was included in this namespace, the conventions used in the Js namespace is still that of very thin bindings.
The type you see for the callback function in the documentation, 'a -> 'b [#bs], and the type you see in the error message, (. 'a) => 'b, are exactly the same type. But the former is in OCaml's syntax while the latter is in Reason's, and also sugared up to look less offensive. Either way, the problem is that you're passing it an ordinary function when it expects this weird other kind of function.
The weird other kind of function is called an uncurried function. It's called that because "normal" functions in Reason are curried, while JavaScript functions are not. An uncurried function is therefore essentially just a native JavaScript function, which you sometimes need to deal with because you might receive one or need to pass one to a higher-order JavaScript function, like those in the Js namespace.
So how do you create an uncurried function in Reason? Just add a ., like in the type:
let optTest: option(int) = Js.Option.map((.x) => x, Some(1);
Or if you want to do it without the sugar (which you don't, but for the sake of completeness):
let optTest: option(int) = Js.Option.map([#bs] x => x, Some(1);
Addendum:
You might have noticed that I've replaced Js.Option.t and Js.Option.some in your example with option and Some. That's because those are the actual primitives. The option type is essentially defined as
type option('a) =
| Some('a)
| None
and is available everywhere.
Js.Option.t('a) (and Belt.Option.t('a)) is just an alias. And Js.Option.some is just a convenience function which doesn't have any equivalent in Belt.Option. They're mostly just there for consistency, and you should normally use the actual type and variant constructors instead.
I am trying to use FakeItEasy to mock an interface defined in C#
public interface IMyInterface
{
int HeartbeatInterval { get; set; }
}
In the F# test i do
let myFake = A.Fake<IMyInterface>()
A.CallTo(fun () -> ((!myFake).HeartbeatInterval)).Returns(10) |> ignore
Running this in the test runner results in
System.ArgumentException
Expression of type 'Microsoft.FSharp.Core.FSharpFunc`2[Microsoft.FSharp.Core.Unit,System.Int32]' cannot be used for return type 'System.Int32'
In fact it would seem that it does this for any return type e.g. if HeartbeatInterval returned type of foo then the exception thrown would be for type foo instead of System.Int32.
Am i doing this wrong or is there some incompatibility between F# and FakeItEasy?
It has crossed my mind that using an Object Expression might be an easier way to go.
I would venture a hypothesis that the "Easy" in "FakeItEasy" stands for "Easy Over Simple". The library says it's "easy", and that is probably true if you're using it from C#. Because it is quite obviously designed for use with that language. But it is far from "simple", because it is using C#-specific syntactic tricks that are hidden from view and don't work in F# well.
The specific gotcha you're getting right now is a combination of two things: (1) F# functions are not the same as C# Func<T,R>, and (2) F# overload resolution rules are different from C#.
There are three overloads of CallTo - two of them take an Expression<_>, and the third one is a "catch-all", taking an object. In C#, if you call this method with a lambda-expression as argument, the compiler will try its best to convert the lambda-expression to an Expression<_> and call one of the specialized methods. F#, however, does not make this effort: F#'s support for C#-style Expression<_> is very limited, primarily focused on compatability with LINQ, and only kicks in when there are no alternatives. So in this case, F# chooses to call the CallTo(object) overload.
Next, what would the argument be? F# is a very strict and consistent language. Apart from some special interop cases, most F# expressions have a definite type, regardless of the context in which they appear. Specifically, an expression of the form fun() -> x will have type unit -> 'a, where 'a is the type of x. In other words, it's an F# function.
At runtime, F# functions are represented by the type FSharpFunc<T,R>, so that is what the compiler will pass to the CallTo(object) method, which will look at it and, unable to understand what the hell it is, throw an exception.
To fix it, you could make yourself a special version of CallTo (let's call it FsCallTo) that would force the F# compiler to convert your fun() -> x expression into an Expression<_>, then use that method instead of CallTo:
// WARNING: unverified code. Should work, but I haven't checked.
type A with
// This is how you declare extension methods in F#
static member FsCallTo( e: System.Linq.Expressions.Expression<System.Func<_,_>> ) = A.CallTo( e )
let myFake = A.Fake<IMyInterface>()
// Calling FsCallTo will force F# to generate an `Expression<_>`,
// because that's what the method expects:
A.FsCallTo(fun () -> ((!myFake).HeartbeatInterval)).Returns(10) |> ignore
However, as you have absolutely correctly observed, this is way too much of a hassle for mocking up an interface, since F# already has a perfectly statically verifiable, runtime-cost-free, syntactically nice alternative in the form of object expressions:
let myFake = { new IMyInterface with
member this.HeartbeatInterval = 10
member this.HeartbeatInterval with set _ = ()
}
I would totally recommend going with them instead.
I can't for the life of me figure out why the following SML function is throwing a Warning in my homework problem:
fun my_func f ls =
case ls of
[] => raise MyException
| head :: rest => case f head of
SOME v => v
| NONE => my_func f rest
fun f a = if isSome a then a else NONE;
Whenever I call my_func with the following test functions:
my_func f [NONE, NONE];
my_func f [];
I always get the warning:
Warning: type vars not generalized because of
value restriction are instantiated to dummy types (X1,X2,...)
Whenever I pass in an options list containing at least one SOME value, this Warning is not thrown. I know it must be something to do with the fact that I am using polymorphism in my function currying, but I've been completely stuck as to how to get rid of these warnings.
Please help if you have any ideas - thank you in advance!
The value restriction referenced in the warning is one of the trickier things to understand in SML, however I will do my best to explain why it comes up in this case and try to point you towards a few resources to learn more.
As you know, SML will use type inference to deduce most of the types in your programs. In this program, the type of my_func will be inferred to be ('a -> 'b option) -> 'a list -> 'b. As you noted, it's a polymorphic type. When you call my_func like this
myfunc f [NONE, SOME 1, NONE];
... the type variables 'a and 'b are instantiated to int option and int.
However when you call it without a value such as SOME 1 above
myfunc f [NONE, NONE];
What do you think the type variables should be instantiated to? The types should be polymorphic -- something like 't option and 't for all types 't. However, there is a limitation which prevents values like this to take on polymorphic types.
SML defines some expressions as non-expansive values and only these values may take on polymorphic types. They are:
literals (constants)
variables
function expressions
constructors (except for ref) applied to non-expansive values
a non-expansive values with a type annotation
tuples where each field is a non-expansive value
records where each field is a non-expansive value
lists where each field is a non-expansive value
All other expressions, notably function calls (which is what the call to my_func is) cannot be polymorphic. Neither can references. You might be curious to see that the following does not raise a warning:
fn () => my_func f [NONE, NONE];
Instead, the type inferred is unit -> 'a. If you were to call this function however, you would get the warning again.
My understanding of the reason for this restriction is a little weak, but I believe that the underlying root issue is mutable references. Here's an example I've taken from the MLton site linked below:
val r: 'a option ref = ref NONE
val r1: string option ref = r
val r2: int option ref = r
val () = r1 := SOME "foo"
val v: int = valOf (!r2)
This program does not typecheck under SML, due to the value restriction. Were it not for the value restriction, this program would have a type error at runtime.
As I said, my understanding is shaky. However I hope I've shed a little light on the issue you've run into, although I believe that in your case you could safely ignore the warning. Here are some references should you decide you'd like to dig deeper:
http://users.cis.fiu.edu/~smithg/cop4555/valrestr.html
http://mlton.org/ValueRestriction
(BTW the MLton site is solid gold. There's so much hidden away here, so if you're trying to understand something weird about SML, I highly recommend searching here because you'll likely turn up a lot more than you initially wanted)
Since it seems like you're actually using SML/NJ, this is a pretty handy guide to the error messages and warnings that it will give you at compile time:
http://flint.cs.yale.edu/cs421/smlnj/doc/errors.html