├── .clang-format ├── .gitignore ├── CMakeLists.txt ├── LICENSE ├── README.md ├── include ├── Free.h ├── Functor.h ├── List.h ├── Monad.h └── void_t.h └── test ├── CMakeLists.txt ├── FreeTest.cpp ├── FunctorTest.cpp ├── ListTest.cpp ├── TestRunner.cpp └── catch.hpp /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language: Cpp 3 | # BasedOnStyle: Google 4 | AccessModifierOffset: -1 5 | AlignAfterOpenBracket: Align 6 | AlignConsecutiveAssignments: true 7 | AlignConsecutiveDeclarations: false 8 | AlignEscapedNewlinesLeft: true 9 | AlignOperands: true 10 | AlignTrailingComments: true 11 | AllowAllParametersOfDeclarationOnNextLine: true 12 | AllowShortBlocksOnASingleLine: false 13 | AllowShortCaseLabelsOnASingleLine: false 14 | AllowShortFunctionsOnASingleLine: All 15 | AllowShortIfStatementsOnASingleLine: true 16 | AllowShortLoopsOnASingleLine: true 17 | AlwaysBreakAfterDefinitionReturnType: None 18 | AlwaysBreakAfterReturnType: None 19 | AlwaysBreakBeforeMultilineStrings: true 20 | AlwaysBreakTemplateDeclarations: true 21 | BinPackArguments: false 22 | BinPackParameters: false 23 | BraceWrapping: 24 | AfterClass: false 25 | AfterControlStatement: false 26 | AfterEnum: false 27 | AfterFunction: false 28 | AfterNamespace: false 29 | AfterObjCDeclaration: false 30 | AfterStruct: false 31 | AfterUnion: false 32 | BeforeCatch: false 33 | BeforeElse: false 34 | IndentBraces: false 35 | BreakBeforeBinaryOperators: None 36 | BreakBeforeBraces: Attach 37 | BreakBeforeTernaryOperators: true 38 | BreakConstructorInitializersBeforeComma: false 39 | ColumnLimit: 90 40 | CommentPragmas: '^ IWYU pragma:' 41 | ConstructorInitializerAllOnOneLineOrOnePerLine: true 42 | ConstructorInitializerIndentWidth: 4 43 | ContinuationIndentWidth: 4 44 | Cpp11BracedListStyle: true 45 | DerivePointerAlignment: false 46 | DisableFormat: false 47 | ExperimentalAutoDetectBinPacking: false 48 | ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH ] 49 | IncludeCategories: 50 | - Regex: '^<.*\.h>' 51 | Priority: 1 52 | - Regex: '^<.*' 53 | Priority: 2 54 | - Regex: '.*' 55 | Priority: 3 56 | IndentCaseLabels: true 57 | IndentWidth: 2 58 | IndentWrappedFunctionNames: false 59 | KeepEmptyLinesAtTheStartOfBlocks: false 60 | MacroBlockBegin: '' 61 | MacroBlockEnd: '' 62 | MaxEmptyLinesToKeep: 1 63 | NamespaceIndentation: All 64 | ObjCBlockIndentWidth: 2 65 | ObjCSpaceAfterProperty: false 66 | ObjCSpaceBeforeProtocolList: false 67 | PenaltyBreakBeforeFirstCallParameter: 1 68 | PenaltyBreakComment: 300 69 | PenaltyBreakFirstLessLess: 120 70 | PenaltyBreakString: 1000 71 | PenaltyExcessCharacter: 1000000 72 | PenaltyReturnTypeOnItsOwnLine: 200 73 | PointerAlignment: Left 74 | ReflowComments: true 75 | SortIncludes: true 76 | SpaceAfterCStyleCast: false 77 | SpaceBeforeAssignmentOperators: true 78 | SpaceBeforeParens: ControlStatements 79 | SpaceInEmptyParentheses: false 80 | SpacesBeforeTrailingComments: 2 81 | SpacesInAngles: false 82 | SpacesInContainerLiterals: true 83 | SpacesInCStyleCastParentheses: false 84 | SpacesInParentheses: false 85 | SpacesInSquareBrackets: false 86 | Standard: Auto 87 | TabWidth: 8 88 | UseTab: Never 89 | ... 90 | 91 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /build*/ 2 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.0) 2 | 3 | project(FreeCpp) 4 | 5 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 6 | 7 | # We use Boost for the header-only variant library 8 | find_package(Boost REQUIRED COMPONENTS) 9 | 10 | # Turn on lots of compiler warnings 11 | if(MSVC) 12 | # Force to always compile with W4 13 | if(CMAKE_CXX_FLAGS MATCHES "/W[0-4]") 14 | string(REGEX REPLACE "/W[0-4]" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 15 | else() 16 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W4") 17 | endif() 18 | elseif(CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX) 19 | # Update if necessary 20 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -pedantic") 21 | endif() 22 | 23 | # Define our header-only Monad library 24 | add_library(Monad INTERFACE) 25 | target_include_directories(Monad INTERFACE include ${Boost_INCLUDE_DIRS}) 26 | # This turns on C++14 mode 27 | target_compile_features(Monad INTERFACE 28 | cxx_alias_templates 29 | cxx_trailing_return_types 30 | cxx_return_type_deduction) 31 | 32 | # Tests 33 | add_subdirectory(test) 34 | 35 | enable_testing() 36 | add_test(NAME Test COMMAND TestRunner) 37 | 38 | # Support for building Markdown from the specially marked up source files. 39 | find_program(CPP2BLOG cpp2blog PATHS /home/toby/.local/bin) 40 | 41 | set(ARTICLE_SOURCES 42 | ${CMAKE_CURRENT_SOURCE_DIR}/include/Functor.h 43 | ${CMAKE_CURRENT_SOURCE_DIR}/include/Monad.h 44 | ${CMAKE_CURRENT_SOURCE_DIR}/include/List.h 45 | ${CMAKE_CURRENT_SOURCE_DIR}/include/Free.h) 46 | add_custom_command( 47 | OUTPUT implementation.md 48 | COMMAND ${CPP2BLOG} ${ARTICLE_SOURCES} >implementation.md 49 | DEPENDS ${ARTICLE_SOURCES} 50 | ) 51 | set(USAGE_SOURCES 52 | ${CMAKE_CURRENT_SOURCE_DIR}/test/FreeTest.cpp) 53 | add_custom_command( 54 | OUTPUT usage.md 55 | COMMAND ${CPP2BLOG} ${USAGE_SOURCES} >usage.md 56 | DEPENDS ${USAGE_SOURCES} 57 | ) 58 | add_custom_target(article 59 | DEPENDS implementation.md usage.md) 60 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Toby Allsopp 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Free Monads in C++ # 2 | 3 | This project is an experiment to see if/how the concept of a Free Monad can be used in C++. 4 | 5 | See my blog post at 6 | for a detailed 7 | explanation. 8 | -------------------------------------------------------------------------------- /include/Free.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | // Copyright (c) 2016 Toby Allsopp 4 | // See LICENSE for license details. 5 | 6 | #include "Functor.h" 7 | #include "Monad.h" 8 | 9 | /*! 10 | ### Free Monads ### 11 | 12 | Every type _f_ that is a Functor has a "Free" Monad. A Free Monad is some 13 | category theory gobbledygook but it's basically the simplest possible Monad 14 | that doesn't throw any information away. 15 | 16 | In Haskell, this is defined quite simply: 17 | 18 | ``` 19 | data Free f a = Return a | Bind (f (Free f a)) 20 | 21 | instance (Functor f) => Monad (Free f) where 22 | ... 23 | ``` 24 | 25 | This is a bit complicated to express in C++ due to the lack of algebraic data 26 | types, in particular the lack of a sum type (`tuple` and `struct` provide 27 | product types). In C++17 we will get `variant` which plugs the gap acceptably 28 | and in the meantime we can use `boost::variant`. 29 | */ 30 | #include 31 | 32 | /*! 33 | To define something like the Haskell `data` with two cases, we define a type 34 | for each case and then combine them into a `variant`. Because the `Bind` 35 | case is recursive we need to forward-declare them and use 36 | `recursive_wrapper` to define the `variant`. 37 | */ 38 | namespace Free { 39 | template