loopperfect / neither Goto Github PK
View Code? Open in Web Editor NEWEither and Maybe monads for better error-handling in C++ ↔️
License: MIT License
Either and Maybe monads for better error-handling in C++ ↔️
License: MIT License
Does this library support polymorphism on the left-hand-side or the right-hand-side of the Either
type? Looking at the code right now, it doesn't look like it because the value will be moved into the supertype. If this is true, I would like to propose this as a feature.
It would probably require some bit-fiddling and storing the data as a raw buffer, but I'm quite certain it can work.
To express the type contracts of (left/right) flatMap and map, we should introduce meta functions for SFINAE.
template<class L, class R>
auto ensureEither( Either<L,R> const& e) - > decltype( e ) {
return e;
}
template<class L, class R>
auto ensureEither( Either<L,R> const& e, L, R ) - > decltype( e ) {
return e;
}
template<class L, class R>
auto ensureEitherLeft( Either<L,R> const& e, L) - > decltype( e ) {
return e;
}
template<class L, class R>
auto ensureEitherRight( Either<L,R> const& e, R) - > decltype( e ) {
return e;
}
This could be used to express the contracts of map:
template<class LeftCase>
auto leftFlatMap(LeftCase leftCase) -> decltype(
ensureEitherRight( leftCase(leftValue), rightValue )
) {
if (!*this) {
return leftCase( leftValue );
}
return NextEither::rightOf(rightValue);
}
Uniform initialization (curly braces initialization) is being used in either.hpp
, and while there should be nothing wrong with that some compilers do not handle this well.
For instance, uniform initialization causes problems with nlohmann's json library compiled with gcc, as backed with this issue: nlohmann/json#985
So, using neither with gcc and json objects causes the latter being modified.
As an excercise I have removed uniform initialization from either.hpp and after that Either wrapped around json I was playing with worked as expected.
I'm using the Visual C++ 19.27.29110 compiler on Windows 10.
❯ bazel test "@com_loopperfect_neither//:all_tests"
INFO: Analyzed 4 targets (0 packages loaded, 0 targets configured).
INFO: Found 4 test targets...
ERROR: C:/tmp/yhlvxsm5/external/com_loopperfect_neither/BUILD.bazel:42:8: C++ compilation of rule '@com_loopperfect_neither//:lift_test' failed (Exit 2): cl.exe failed: error executing command
cd C:/tmp/yhlvxsm5/execroot/__main__
SET INCLUDE=C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\ATLMFC\include;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\include;C:\Program Files (x86)\Windows Kits\NETFXSDK\4.8\include\um;C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\ucrt;C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\shared;C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\um;C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\winrt;C:\Program Files (x86)\Windows Kits\10\include\10.0.19041.0\cppwinrt
SET PATH=C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\\Extensions\Microsoft\IntelliCode\CLI;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\VC\Tools\MSVC\14.27.29110\bin\HostX64\x64;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\VC\VCPackages;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\CommonExtensions\Microsoft\TestWindow;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\CommonExtensions\Microsoft\TeamFoundation\Team Explorer;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\MSBuild\Current\bin\Roslyn;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Team Tools\Performance Tools\x64;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Team Tools\Performance Tools;C:\Program Files (x86)\Microsoft Visual Studio\Shared\Common\VSPerfCollectionTools\vs2019\\x64;C:\Program Files (x86)\Microsoft Visual Studio\Shared\Common\VSPerfCollectionTools\vs2019\;C:\Program Files (x86)\Microsoft SDKs\Windows\v10.0A\bin\NETFX 4.8 Tools\x64\;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\CommonExtensions\Microsoft\FSharp\;C:\Program Files (x86)\Windows Kits\10\bin\10.0.19041.0\x64;C:\Program Files (x86)\Windows Kits\10\bin\x64;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\\MSBuild\Current\Bin;C:\Windows\Microsoft.NET\Framework64\v4.0.30319;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\Tools\;;C:\WINDOWS\system32;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\CMake\bin;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\CommonExtensions\Microsoft\CMake\Ninja;C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\VC\Linux\bin\ConnectionManagerExe
SET PWD=/proc/self/cwd
SET TEMP=C:\Users\yesudeep\AppData\Local\Temp
SET TMP=C:\Users\yesudeep\AppData\Local\Temp
C:/Program Files (x86)/Microsoft Visual Studio/2019/Community/VC/Tools/MSVC/14.27.29110/bin/HostX64/x64/cl.exe /nologo /DCOMPILER_MSVC /DNOMINMAX /D_WIN32_WINNT=0x0601 /D_CRT_SECURE_NO_DEPRECATE /D_CRT_SECURE_NO_WARNINGS /bigobj /Zm500 /EHsc /wd4351 /wd4291 /wd4250 /wd4996 /Iexternal/com_loopperfect_neither /Ibazel-out/x64_windows-fastbuild/bin/external/com_loopperfect_neither /Iexternal/com_google_googletest /Ibazel-out/x64_windows-fastbuild/bin/external/com_google_googletest /Iexternal/bazel_tools /Ibazel-out/x64_windows-fastbuild/bin/external/bazel_tools /Ibazel-out/x64_windows-fastbuild/bin/external/com_loopperfect_neither/_virtual_includes/neither /Iexternal/com_loopperfect_neither/neither/include /Ibazel-out/x64_windows-fastbuild/bin/external/com_loopperfect_neither/neither/include /Iexternal/com_google_googletest/googlemock /Ibazel-out/x64_windows-fastbuild/bin/external/com_google_googletest/googlemock /Iexternal/com_google_googletest/googlemock/include /Ibazel-out/x64_windows-fastbuild/bin/external/com_google_googletest/googlemock/include /Iexternal/com_google_googletest/googletest /Ibazel-out/x64_windows-fastbuild/bin/external/com_google_googletest/googletest /Iexternal/com_google_googletest/googletest/include /Ibazel-out/x64_windows-fastbuild/bin/external/com_google_googletest/googletest/include /showIncludes /MD /Od /Z7 /wd4117 -D__DATE__="redacted" -D__TIMESTAMP__="redacted" -D__TIME__="redacted" -DHAVE_BAZEL_BUILD /DHAVE_BAZEL_BUILD_WINDOWS /D_HAS_DEPRECATED_RESULT_OF=1 /std:c++latest /Fobazel-out/x64_windows-fastbuild/bin/external/com_loopperfect_neither/_objs/lift_test/lift.obj /c external/com_loopperfect_neither/neither/tests/lift.cpp
Execution platform: @local_config_platform//:host
C:\tmp\yhlvxsm5\execroot\__main__\bazel-out\x64_windows-fastbuild\bin\external\com_loopperfect_neither\_virtual_includes\neither\neither/either.hpp(149): error C2560: 'common_type<L2,R2>::type neither::Either<L,R>::join(void) &&': cannot overload a member function with ref-qualifier with a member function without ref-qualifier
C:\tmp\yhlvxsm5\execroot\__main__\bazel-out\x64_windows-fastbuild\bin\external\com_loopperfect_neither\_virtual_includes\neither\neither/either.hpp(304): note: see reference to class template instantiation 'neither::Either<L,R>' being compiled
INFO: Elapsed time: 2.906s, Critical Path: 1.80s
INFO: 0 processes.
FAILED: Build did NOT complete successfully
@com_loopperfect_neither//:either_test NO STATUS
@com_loopperfect_neither//:maybe_test NO STATUS
@com_loopperfect_neither//:try_test NO STATUS
@com_loopperfect_neither//:lift_test FAILED TO BUILD
FAILED: Build did NOT complete successfully
Bazel build file for reference:
load("@rules_cc//cc:defs.bzl", "cc_library", "cc_test")
licenses(["notice"]) # MIT License
exports_files(["LICENSE.txt"])
cc_library(
name = "neither",
hdrs = [
"neither/include/either.hpp",
"neither/include/lift.hpp",
"neither/include/maybe.hpp",
"neither/include/neither.hpp",
"neither/include/traits.hpp",
"neither/include/try.hpp",
],
include_prefix = "neither",
includes = ["neither/include"],
linkstatic = True,
strip_include_prefix = "neither/include",
visibility = ["//visibility:public"],
)
test_suite(
name = "all_tests",
tests = [
":either_test",
":lift_test",
":maybe_test",
":try_test",
],
)
cc_test(
name = "either_test",
size = "small",
srcs = [
"neither/tests/either.cpp",
],
deps = [
":neither",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "lift_test",
size = "small",
srcs = [
"neither/tests/lift.cpp",
],
deps = [
":neither",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "maybe_test",
size = "small",
srcs = [
"neither/tests/maybe.cpp",
],
deps = [
":neither",
"@com_google_googletest//:gtest_main",
],
)
cc_test(
name = "try_test",
size = "small",
srcs = [
"neither/tests/try.cpp",
],
deps = [
":neither",
"@com_google_googletest//:gtest_main",
],
)
int add(int x, int y) {
return x+y;
}
auto x = maybe(1);
auto y = maybe(2);
auto maybeAdd = lift(add);
auto maybeZ = maybeAdd(x, y);
I would really like to use this implementation in my project but there is no license defined. Without it standard copyright laws hold. Is there any chance for some permissive license?
A Maybe is a container like vector but has either 0 or 1 element.
This would be convenient:
for(auto x : maybe(1) ) {
std::cout << x << std::endl; // prints 1
}
for(auto x : maybe<int>() ) {
//loop not entered
}
Functional maps should never have sideeffects. For sideeffects this way should be preferred.
Hey neither developers, Hey @nikhedonia,
so I contributed #30 some time ago and promised to implement some move specializations for neither::Maybe
which I just started. However, I was not a 100% convinced of my implementation for Eithers at that time and now ran into more issues with neither::Maybe
. Mostly that the hasValue attribute needed to be mutable now.
My main concern is that pull #30 introduced mutations to Eithers, which are not directly visible to the developer. Given that a stored value is not copy-constructible the Either would lose its value on map & join. At that time I thought this was the pill one had to swallow to work with unique_ptrs in lvalues.
Now it dawned on me that you can always std::move
the object to retrieve an rvalue again.
Here is a very minimal example:
TEST(neither, either_fromLvalueByMove) {
neither::Either<std::unique_ptr<int>, int> e = left(std::make_unique<int>(0));
std::move(e).leftMap([](auto ptr) {
EXPECT_EQ(*ptr, 0);
return std::move(ptr);
});
}
From my POV this is much more clear as it shows clearly that e
is potentially no longer usable after this operation. Therefore I would suggest to revert this pull and to just add some documentation on some common and niche use cases to the documentation.
Kind regards
Tilmann
Currently either is never constexpr because it implements a destructor.
This is needed in the generic case as the types for left or right might have a custom destructor.
Using type_traits we can test if this is the case and make Either a literal type(constexpr) if we deal with pods only.
The copy constructors,
constexpr Either( Either<L, R> const& e )
: isLeft(e.isLeft) {
if(isLeft) {
leftValue = e.leftValue;
} else {
rightValue = e.rightValue;
}
}
constexpr Maybe(Maybe<T> const &o) : hasValue{o.hasValue} {
if (o.hasValue) {
value = o.value;
}
}
Are not implemented correctly see: https://stackoverflow.com/q/44316175/1104294. this->leftValue
this->rightValue
, and this->value
are being directly assigned to instead of copy-constructed as they should be.
We should have an overload for move semantics for either.
However we need to be careful here. Move mutates the state of the referenced object by making it potentially invalid.
In case of an Either this should be only allowed if Either itself is movable to make Either behave like an valuetype.
In c++ this semantics can be expressed via:
#include <iostream>
#include <string>
struct Test {
constexpr int get() const& { return 1; }
constexpr int get()&& { return 0; } // if object is a temporary
};
int main()
{
Test x;
std::cout << "const&, " << x.get() << "!\n";
std::cout << "&& " << Test{}.get() << "!\n";
}
// http://cpp.sh/6tnb6
Why is this Important? - consider the following example:
Eiher<int, unique_ptr<int>> e = right(make_unique(5));
auto e2 = e.rightMap([](auto&& ptr) { return ptr; });
auto e3 = e.rightMap([](auto&& ptr) { return ptr; });
e2.rightMap([](auto&& ptr) { return ptr.get() == 5; }); // right(true);
e2.rightMap([](auto&& ptr) { return ptr.get() == 5; }); // segfault: ptr is not valid anymore
Hi,
I assumed that it should be composable, i.e. something like should work:
struct my_error_t {
std::string s;
};
struct struct_a {
int v;
};
struct struct_b {
double v;
};
neither::Either<my_error_t, struct_a> f1();
neither::Either<my_error_t, struct_b> f2();
void code() {
auto value = f1()
.rightMap([](const struct_a& v){
return f2();
})
.rightMap([](const struct_b& v){
return 5;
})
.leftMap([](const auto& my_error){
return 6;
}).join();
// value should be either 5 or 6
}
To let it compile, I have to write something like :
auto value = f1()
.rightMap([](const struct_a& v){
return f2();
})
.rightMap([](const auto& v){
return v.rightMap([](const struct_b& v){
return 5;
})
.leftMap([](const auto& my_error){
return 6;
});
})
.leftMap([](const auto& my_error){
return 6;
}).join();
// value should be either 5 or 6
what is the sense of the library if it is not composable, i.e. forces me to unwrap in the next right_map
the monadic result of the previous right_map
and duplicate errors handling ?
WBR,
basiliscos
current api requires you to write:
Maybe<int> maybeMeaningOfLife(int x) {
if(x<0) return maybe(); // none; would be better
return maybe(42);
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.