Browse Source

Optional Stream - represents a series of computation steps where one or more steps may not return a value on a failure case. It provides two transformations 'map :: T -> R', and 'flatmap :: T -> optional<R>'. If the value in the stream is not present, the function provided to map/flatmap will not be invoked. In this manner, the end result of a chain will be either the computed value, or <absent> if any flatmap returns failure.

Samuel Jaffe 8 years ago
commit
51e734d7bd
3 changed files with 398 additions and 0 deletions
  1. 283 0
      optional.stream.xcodeproj/project.pbxproj
  2. 70 0
      optional_stream.hpp
  3. 45 0
      optional_stream.t.h

+ 283 - 0
optional.stream.xcodeproj/project.pbxproj

@@ -0,0 +1,283 @@
+// !$*UTF8*$!
+{
+	archiveVersion = 1;
+	classes = {
+	};
+	objectVersion = 46;
+	objects = {
+
+/* Begin PBXBuildFile section */
+		CDF937521E3ED1C3003E5D5C /* optional_stream_tc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = CDF937511E3ED1C3003E5D5C /* optional_stream_tc.cpp */; };
+/* End PBXBuildFile section */
+
+/* Begin PBXCopyFilesBuildPhase section */
+		CDF9373D1E3D793F003E5D5C /* CopyFiles */ = {
+			isa = PBXCopyFilesBuildPhase;
+			buildActionMask = 2147483647;
+			dstPath = /usr/share/man/man1/;
+			dstSubfolderSpec = 0;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 1;
+		};
+/* End PBXCopyFilesBuildPhase section */
+
+/* Begin PBXFileReference section */
+		CDF9373F1E3D793F003E5D5C /* optional_stream_tc */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = optional_stream_tc; sourceTree = BUILT_PRODUCTS_DIR; };
+		CDF9374B1E3D797D003E5D5C /* optional_stream.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = optional_stream.hpp; sourceTree = "<group>"; };
+		CDF9374F1E3ED13A003E5D5C /* optional_stream.t.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = optional_stream.t.h; sourceTree = "<group>"; };
+		CDF937511E3ED1C3003E5D5C /* optional_stream_tc.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = optional_stream_tc.cpp; sourceTree = "<group>"; };
+/* End PBXFileReference section */
+
+/* Begin PBXFrameworksBuildPhase section */
+		CDF9373C1E3D793F003E5D5C /* Frameworks */ = {
+			isa = PBXFrameworksBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXFrameworksBuildPhase section */
+
+/* Begin PBXGroup section */
+		CDF937361E3D793F003E5D5C = {
+			isa = PBXGroup;
+			children = (
+				CDF937491E3D795F003E5D5C /* src */,
+				CDF9374A1E3D7962003E5D5C /* test */,
+				CDF937401E3D793F003E5D5C /* Products */,
+			);
+			sourceTree = "<group>";
+		};
+		CDF937401E3D793F003E5D5C /* Products */ = {
+			isa = PBXGroup;
+			children = (
+				CDF9373F1E3D793F003E5D5C /* optional_stream_tc */,
+			);
+			name = Products;
+			sourceTree = "<group>";
+		};
+		CDF937491E3D795F003E5D5C /* src */ = {
+			isa = PBXGroup;
+			children = (
+				CDF9374B1E3D797D003E5D5C /* optional_stream.hpp */,
+			);
+			name = src;
+			sourceTree = "<group>";
+		};
+		CDF9374A1E3D7962003E5D5C /* test */ = {
+			isa = PBXGroup;
+			children = (
+				CDF9374F1E3ED13A003E5D5C /* optional_stream.t.h */,
+				CDF937511E3ED1C3003E5D5C /* optional_stream_tc.cpp */,
+			);
+			name = test;
+			sourceTree = "<group>";
+		};
+/* End PBXGroup section */
+
+/* Begin PBXNativeTarget section */
+		CDF9373E1E3D793F003E5D5C /* optional_stream_tc */ = {
+			isa = PBXNativeTarget;
+			buildConfigurationList = CDF937461E3D793F003E5D5C /* Build configuration list for PBXNativeTarget "optional_stream_tc" */;
+			buildPhases = (
+				CDF937501E3ED193003E5D5C /* ShellScript */,
+				CDF9373B1E3D793F003E5D5C /* Sources */,
+				CDF9373C1E3D793F003E5D5C /* Frameworks */,
+				CDF9373D1E3D793F003E5D5C /* CopyFiles */,
+			);
+			buildRules = (
+			);
+			dependencies = (
+			);
+			name = optional_stream_tc;
+			productName = optional.stream;
+			productReference = CDF9373F1E3D793F003E5D5C /* optional_stream_tc */;
+			productType = "com.apple.product-type.tool";
+		};
+/* End PBXNativeTarget section */
+
+/* Begin PBXProject section */
+		CDF937371E3D793F003E5D5C /* Project object */ = {
+			isa = PBXProject;
+			attributes = {
+				LastUpgradeCheck = 0720;
+				ORGANIZATIONNAME = "Sam Jaffe";
+				TargetAttributes = {
+					CDF9373E1E3D793F003E5D5C = {
+						CreatedOnToolsVersion = 7.2.1;
+					};
+				};
+			};
+			buildConfigurationList = CDF9373A1E3D793F003E5D5C /* Build configuration list for PBXProject "optional.stream" */;
+			compatibilityVersion = "Xcode 3.2";
+			developmentRegion = English;
+			hasScannedForEncodings = 0;
+			knownRegions = (
+				en,
+			);
+			mainGroup = CDF937361E3D793F003E5D5C;
+			productRefGroup = CDF937401E3D793F003E5D5C /* Products */;
+			projectDirPath = "";
+			projectRoot = "";
+			targets = (
+				CDF9373E1E3D793F003E5D5C /* optional_stream_tc */,
+			);
+		};
+/* End PBXProject section */
+
+/* Begin PBXShellScriptBuildPhase section */
+		CDF937501E3ED193003E5D5C /* ShellScript */ = {
+			isa = PBXShellScriptBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+			);
+			inputPaths = (
+				"$(SRCROOT)/optional_stream.t.h",
+				"$(SRCROOT)/either_stream.t.h",
+			);
+			outputPaths = (
+				"$(SRCROOT)/optional_stream_tc.cpp",
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+			shellPath = /bin/sh;
+			shellScript = "cxxtestgen --error-printer -o optional_stream_tc.cpp optional_stream.t.h either_stream.t.h";
+		};
+/* End PBXShellScriptBuildPhase section */
+
+/* Begin PBXSourcesBuildPhase section */
+		CDF9373B1E3D793F003E5D5C /* Sources */ = {
+			isa = PBXSourcesBuildPhase;
+			buildActionMask = 2147483647;
+			files = (
+				CDF937521E3ED1C3003E5D5C /* optional_stream_tc.cpp in Sources */,
+			);
+			runOnlyForDeploymentPostprocessing = 0;
+		};
+/* End PBXSourcesBuildPhase section */
+
+/* Begin XCBuildConfiguration section */
+		CDF937441E3D793F003E5D5C /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "-";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = dwarf;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				ENABLE_TESTABILITY = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_DYNAMIC_NO_PIC = NO;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_OPTIMIZATION_LEVEL = 0;
+				GCC_PREPROCESSOR_DEFINITIONS = (
+					"DEBUG=1",
+					"$(inherited)",
+				);
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				MTL_ENABLE_DEBUG_INFO = YES;
+				ONLY_ACTIVE_ARCH = YES;
+				SDKROOT = macosx;
+			};
+			name = Debug;
+		};
+		CDF937451E3D793F003E5D5C /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				ALWAYS_SEARCH_USER_PATHS = NO;
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
+				CLANG_CXX_LIBRARY = "libc++";
+				CLANG_ENABLE_MODULES = YES;
+				CLANG_ENABLE_OBJC_ARC = YES;
+				CLANG_WARN_BOOL_CONVERSION = YES;
+				CLANG_WARN_CONSTANT_CONVERSION = YES;
+				CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
+				CLANG_WARN_EMPTY_BODY = YES;
+				CLANG_WARN_ENUM_CONVERSION = YES;
+				CLANG_WARN_INT_CONVERSION = YES;
+				CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
+				CLANG_WARN_UNREACHABLE_CODE = YES;
+				CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+				CODE_SIGN_IDENTITY = "-";
+				COPY_PHASE_STRIP = NO;
+				DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
+				ENABLE_NS_ASSERTIONS = NO;
+				ENABLE_STRICT_OBJC_MSGSEND = YES;
+				GCC_C_LANGUAGE_STANDARD = gnu99;
+				GCC_NO_COMMON_BLOCKS = YES;
+				GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
+				GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
+				GCC_WARN_UNDECLARED_SELECTOR = YES;
+				GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
+				GCC_WARN_UNUSED_FUNCTION = YES;
+				GCC_WARN_UNUSED_VARIABLE = YES;
+				MACOSX_DEPLOYMENT_TARGET = 10.10;
+				MTL_ENABLE_DEBUG_INFO = NO;
+				SDKROOT = macosx;
+			};
+			name = Release;
+		};
+		CDF937471E3D793F003E5D5C /* Debug */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				HEADER_SEARCH_PATHS = /usr/local/include/;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				USER_HEADER_SEARCH_PATHS = "";
+			};
+			name = Debug;
+		};
+		CDF937481E3D793F003E5D5C /* Release */ = {
+			isa = XCBuildConfiguration;
+			buildSettings = {
+				CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
+				HEADER_SEARCH_PATHS = /usr/local/include/;
+				PRODUCT_NAME = "$(TARGET_NAME)";
+				USER_HEADER_SEARCH_PATHS = "";
+			};
+			name = Release;
+		};
+/* End XCBuildConfiguration section */
+
+/* Begin XCConfigurationList section */
+		CDF9373A1E3D793F003E5D5C /* Build configuration list for PBXProject "optional.stream" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CDF937441E3D793F003E5D5C /* Debug */,
+				CDF937451E3D793F003E5D5C /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+		CDF937461E3D793F003E5D5C /* Build configuration list for PBXNativeTarget "optional_stream_tc" */ = {
+			isa = XCConfigurationList;
+			buildConfigurations = (
+				CDF937471E3D793F003E5D5C /* Debug */,
+				CDF937481E3D793F003E5D5C /* Release */,
+			);
+			defaultConfigurationIsVisible = 0;
+			defaultConfigurationName = Release;
+		};
+/* End XCConfigurationList section */
+	};
+	rootObject = CDF937371E3D793F003E5D5C /* Project object */;
+}

+ 70 - 0
optional_stream.hpp

@@ -0,0 +1,70 @@
+//
+//  optional_stream.hpp
+//  optional.stream
+//
+//  Created by Sam Jaffe on 1/28/17.
+//
+
+#pragma once
+
+#if __cplusplus > 201402L
+#include <optional>
+#else
+#include <experimental/optional>
+namespace std { using experimental::optional; }
+using std::experimental::nullopt;
+#endif
+
+namespace stream { namespace optional {
+  namespace detail {
+    template <typename T> struct flatmap_impl_t;
+    template <typename T>
+    struct flatmap_impl_t<std::optional<T>> { using type = T; };
+    
+    template <typename T>
+    class optional_stream {
+    private:
+      template <typename F>
+      using map_f = decltype(std::declval<F>()(std::declval<T>()));
+      template <typename F>
+      using flatmap_f = typename flatmap_impl_t<map_f<F>>::type;
+    public:
+      explicit optional_stream() : value(nullopt) {}
+      explicit optional_stream(T const & v) : value(v) {}
+      explicit optional_stream(T && v) : value(std::forward<T>(v)) {}
+      explicit optional_stream(std::optional<T> const & v) : value(v) {}
+      explicit optional_stream(std::optional<T> && v) : value(std::forward<std::optional<T>>(v)) {}
+      
+      operator std::optional<T>() const { return value; }
+      
+      template <typename F>
+      optional_stream<map_f<F>> map(F && fun) const {
+        using next_t = optional_stream<map_f<F>>;
+        return !value ? next_t{} : next_t{fun(*value)};
+      }
+      
+      template <typename F>
+      optional_stream<flatmap_f<F>> flatmap(F && fun) const {
+        using next_t = optional_stream<flatmap_f<F>>;
+        return !value ? next_t{} : next_t{fun(*value)};
+      }
+    private:
+      std::optional<T> value;
+    };
+  }
+  
+  template <typename T>
+  auto make_stream(std::optional<T> const & opt) -> detail::optional_stream<T> {
+    return detail::optional_stream<T>{opt};
+  }
+  
+  template <typename T>
+  auto make_stream(T const & val) -> detail::optional_stream<T> {
+    return detail::optional_stream<T>{val};
+  }
+  
+  template <typename T>
+  auto make_stream() -> detail::optional_stream<T> {
+    return detail::optional_stream<T>{};
+  }
+} }

+ 45 - 0
optional_stream.t.h

@@ -0,0 +1,45 @@
+//
+//  optional_stream.t.h
+//  optional.stream
+//
+//  Created by Sam Jaffe on 1/29/17.
+//
+
+#pragma once
+
+#include <cxxtest/TestSuite.h>
+
+#include "optional_stream.hpp"
+
+class optional_stream_TestSuite : public CxxTest::TestSuite {
+public:
+  void test_optional_processes_real() {
+    auto strm = stream::optional::make_stream(5);
+    auto plus2 = [](int i) { return i+2; };
+    std::optional<int> out = strm.map(plus2);
+    TS_ASSERT(out);
+    TS_ASSERT_EQUALS(*out, 7);
+  }
+  
+  void test_optional_ignores_fake() {
+    auto strm = stream::optional::make_stream<int>();
+    auto fail = [](int i) { TS_FAIL("Expected Empty"); return i; };
+    std::optional<int> out = strm.map(fail);
+    TS_ASSERT(!out);
+  }
+  
+  void test_optional_flatmap_can_become_empty() {
+    auto strm = stream::optional::make_stream(5);
+    auto discard_odd = [](int i) { return i%2==0? std::optional<int>(i) : nullopt; };
+    std::optional<int> out = strm.flatmap(discard_odd);
+    TS_ASSERT(!out);
+  }
+
+  void test_optional_flatmap_can_remain_exists() {
+    auto strm = stream::optional::make_stream(6);
+    auto discard_odd = [](int i) { return i%2==0? std::optional<int>(i) : nullopt; };
+    std::optional<int> out = strm.flatmap(discard_odd);
+    TS_ASSERT(out);
+    TS_ASSERT_EQUALS(*out, 6);
+  }
+};