I'm writing some relatively functional style code in C , and am trying to avoid inventing a bad name for it. To that end I'm trying to stick to the names of isomorphic concepts in Haskell where possible. (To be clear I don't know a lot of Haskell, but reading about it has been quite illuminating.)
If I've done the Haskell typing correctly, I believe the concept I'm looking for the name of would have this signature:
someName :: (Monad m1, Monad m2) => (a -> m1 b) -> (m2 a -> m1 m2 b)
The idea is that you have a function suitable for using to bind a monad of type m1
, and you transform it into a function that is suitable for binding m1
values that contain m2
values.
I can't write Haskell well enough to give an example, but here is pseudo-C illustrating what I want, using std::optional
and absl::StatusOr
:
// A silly example of a bind function for absl::StatusOr<int>.
absl::StatusOr<int> AddTwoUnless17(int x) {
if (x == 17) {
return absl::InternalError("can't add two to 17!")
}
return x 2;
}
// Convert AddTwoUnless17 into a bind function for
// absl::StatusOr<std::optional<int>>. The resulting logic is:
//
// * If the input is an error, propagate that error.
// * If the input is std::nullopt, return std::nullopt.
// * If the input is 17, return an error.
// * Otherwise return the input plus two.
//
// Here we have:
//
// m1: absl::StatusOr
// m2: std::optional
//
std::function<absl::StatusOr<std::optional<int>>(std::optional<int>) f =
someName(AddTwoUnless17);
CHECK_EQ(
absl::CancelledError(),
f(absl::StatusOr<std::optional<int>>(absl::CancelledError())));
CHECK_EQ(
std::nullopt,
f(absl::StatusOr<std::optional<int>>(std::nullopt)));
CHECK_EQ(
absl::InternalError("can't add two to 17!"),
f(absl::StatusOr<std::optional<int>>(17)));
CHECK_EQ(
13,
f(absl::StatusOr<std::optional<int>>(11)));
Is there a name for this concept? If not, is it because it is flawed in some way, or can be built out of simpler pieces?
CodePudding user response:
The type signature you wrote is basically the same as:
traverse :: (Traversable t, Applicative f) => (a -> f b) -> t a -> f (t b)
where f = m1
and t = m2
.
But it doesn't always work if m2
is an arbitrary monad. It must be Traversable
.