├── .gitattributes ├── .gitignore ├── .travis.yml ├── README.md ├── base ├── build.gradle └── src │ ├── main │ └── groovy │ │ └── com │ │ └── github │ │ └── mperry │ │ └── fg │ │ └── YCombinator.groovy │ └── test │ └── groovy │ └── com │ └── github │ └── mperry │ └── fg │ └── YCombinatorTest.groovy ├── build.gradle ├── consume ├── build.gradle ├── scripts │ ├── bugImplicitBoxing.groovy │ ├── exceptionHandling.groovy │ ├── genericTypeCheck.groovy │ ├── listMonadTest.groovy │ ├── optionMonadTest.groovy │ ├── setMonadTest.groovy │ └── test.groovy └── src │ └── test │ └── groovy │ └── com │ └── github │ └── mperry │ └── fg │ ├── Example.groovy │ └── FutureTest.groovy ├── core ├── build.gradle └── src │ ├── main │ ├── groovy │ │ └── com │ │ │ └── github │ │ │ └── mperry │ │ │ └── fg │ │ │ ├── ClassExtension.groovy │ │ │ ├── CollectionExtension.groovy │ │ │ ├── EitherExtension.groovy │ │ │ ├── F2Extension.groovy │ │ │ ├── F2StaticExtension.groovy │ │ │ ├── F3Extension.groovy │ │ │ ├── F3StaticExtension.groovy │ │ │ ├── F4Extension.groovy │ │ │ ├── F4StaticExtension.groovy │ │ │ ├── F5Extension.groovy │ │ │ ├── F5StaticExtension.groovy │ │ │ ├── F6Extension.groovy │ │ │ ├── F6StaticExtension.groovy │ │ │ ├── F7Extension.groovy │ │ │ ├── F7StaticExtension.groovy │ │ │ ├── F8Extension.groovy │ │ │ ├── F8StaticExtension.groovy │ │ │ ├── FExtension.groovy │ │ │ ├── FStaticExtension.groovy │ │ │ ├── IntegerExtension.groovy │ │ │ ├── ListFJExtension.groovy │ │ │ ├── ListJavaExtension.groovy │ │ │ ├── ListJavaStaticExtension.groovy │ │ │ ├── ListOps.groovy │ │ │ ├── ObjectCompanion.groovy │ │ │ ├── ObjectExtension.groovy │ │ │ ├── OptionExtension.groovy │ │ │ ├── OptionStaticExtension.groovy │ │ │ ├── P1Extension.groovy │ │ │ ├── Rand.groovy │ │ │ ├── ShowExtension.groovy │ │ │ ├── ShowStaticExtension.groovy │ │ │ ├── ShowWorkaroundGroovy.groovy │ │ │ ├── StreamExtension.groovy │ │ │ └── test │ │ │ ├── ArbitraryCompanion.groovy │ │ │ ├── ArbitraryExtension.groovy │ │ │ ├── ArbitraryStaticExtension.groovy │ │ │ ├── ArgExtension.groovy │ │ │ ├── ArgStaticExtension.groovy │ │ │ ├── CheckResultCompanion.groovy │ │ │ ├── CheckResultExtension.groovy │ │ │ ├── CheckResultStaticExtension.groovy │ │ │ ├── CoarbitraryCompanion.groovy │ │ │ ├── GenExtension.groovy │ │ │ ├── GenStaticExtension.groovy │ │ │ └── PropertyExtension.groovy │ ├── java │ │ └── com │ │ │ └── github │ │ │ └── mperry │ │ │ └── ShowWorkaroundJava.java │ └── resources │ │ └── META-INF │ │ └── services │ │ └── org.codehaus.groovy.runtime.ExtensionModule │ └── test │ └── groovy │ └── com │ └── github │ └── mperry │ └── fg │ └── RandTest.groovy ├── demo ├── build.gradle └── src │ ├── main │ └── groovy │ │ └── com │ │ └── github │ │ └── mperry │ │ └── fg │ │ ├── ExceptionHandling.groovy │ │ ├── FileList.groovy │ │ ├── Keno.groovy │ │ ├── MyTest.groovy │ │ ├── PaperScissorsRock.groovy │ │ ├── SimpleIODemoFunctional.groovy │ │ ├── SimpleIODemoImperative.groovy │ │ ├── StateGame.groovy │ │ ├── Test1.groovy │ │ ├── di │ │ ├── App.groovy │ │ ├── Config.groovy │ │ ├── ConfigReader.groovy │ │ ├── User.groovy │ │ └── UserRepository.groovy │ │ ├── state │ │ ├── Input.groovy │ │ ├── Machine.groovy │ │ └── MachineSimulation.groovy │ │ └── trampoline │ │ └── TrampolineClosureTest.groovy │ └── test │ └── groovy │ └── com │ └── github │ └── mperry │ └── fg │ ├── KenoTest.groovy │ ├── TrampolineTest.groovy │ └── state │ └── MachineTest.groovy ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── java8 ├── build.gradle └── src │ └── main │ ├── groovy │ └── com │ │ └── github │ │ └── mperry │ │ └── fg │ │ ├── Function1Extension.groovy │ │ ├── Function2Extension.groovy │ │ ├── OptionalExtension.groovy │ │ └── OptionalStaticExtension.groovy │ └── resources │ └── META-INF │ └── services │ └── org.codehaus.groovy.runtime.ExtensionModule ├── kind ├── build.gradle └── src │ └── main │ └── groovy │ └── com │ └── github │ └── mperry │ └── fg │ ├── FlatMap.groovy │ ├── Free.groovy │ ├── FreeP1.groovy │ ├── FreeP1Monad.groovy │ ├── Return.groovy │ └── Suspend.groovy ├── main ├── build.gradle └── src │ ├── main │ ├── groovy │ │ └── com │ │ │ └── github │ │ │ └── mperry │ │ │ └── fg │ │ │ ├── CollectionExtension2.groovy │ │ │ ├── CollectionStaticExtension2.groovy │ │ │ ├── Comprehension.groovy │ │ │ ├── FutureExtension.groovy │ │ │ ├── FutureMonad.groovy │ │ │ ├── Generator.groovy │ │ │ ├── IOConstants.groovy │ │ │ ├── Identity.groovy │ │ │ ├── IdentityM.groovy │ │ │ ├── IdentityMonad.groovy │ │ │ ├── Lens.groovy │ │ │ ├── ListMonadExtension.groovy │ │ │ ├── ListMonadStaticExtension.groovy │ │ │ ├── RandStateInteger.groovy │ │ │ ├── ReaderM.groovy │ │ │ ├── SetExtension2.groovy │ │ │ ├── SetMonadExtension.groovy │ │ │ ├── SetMonadStaticExtension.groovy │ │ │ ├── SetStaticExtension2.groovy │ │ │ ├── SimpleIO.java │ │ │ ├── SimpleIOExtension.groovy │ │ │ ├── SimpleIOStaticExtension.groovy │ │ │ ├── SkiCalculus.groovy │ │ │ ├── SqlExtension.groovy │ │ │ ├── SqlExtensionJava.java │ │ │ ├── State.groovy │ │ │ ├── StateInt.groovy │ │ │ ├── StateIntMonad.groovy │ │ │ ├── StreamExtension2.groovy │ │ │ ├── StreamStaticExtension2.groovy │ │ │ ├── TypeLambda.groovy │ │ │ ├── WriterM.groovy │ │ │ ├── Yield.groovy │ │ │ └── test │ │ │ ├── DbcContractValidator.groovy │ │ │ ├── MethodVerifier.groovy │ │ │ ├── Model.groovy │ │ │ └── Specification.groovy │ └── resources │ │ └── META-INF │ │ └── services │ │ └── org.codehaus.groovy.runtime.ExtensionModule │ └── test │ ├── groovy │ └── com │ │ └── github │ │ └── mperry │ │ └── fg │ │ ├── ComprehensionTest.groovy │ │ ├── FTest.groovy │ │ ├── IdentityTest.groovy │ │ ├── IdentityTest2.groovy │ │ ├── LensTest.groovy │ │ ├── LiftTest.groovy │ │ ├── ListMonadTest.groovy │ │ ├── ListTest.groovy │ │ ├── MonadLaws.groovy │ │ ├── ObjectTest.groovy │ │ ├── OptionMonadTest.groovy │ │ ├── ReaderTest.groovy │ │ ├── SimpleIOTest.groovy │ │ ├── SqlTest.groovy │ │ ├── StackOverflowTest.groovy │ │ ├── StateDynamicMonadTest.groovy │ │ ├── StateIntDynamicMonadTest.groovy │ │ ├── StateIntMonadTest.groovy │ │ ├── StateTest.groovy │ │ ├── StreamStaticExtension2Test.groovy │ │ ├── StreamTest.groovy │ │ ├── WriterTest.groovy │ │ ├── euler │ │ ├── P01.groovy │ │ ├── P02.groovy │ │ ├── P03.groovy │ │ ├── P04.groovy │ │ └── P05.groovy │ │ └── test │ │ ├── AdditionCommutesTest.groovy │ │ ├── IntegerOverflow.groovy │ │ ├── ListFunctorLawsTest.groovy │ │ ├── ListTest.groovy │ │ └── dbc │ │ ├── ExceptionFreeStack.groovy │ │ ├── ExceptionFreeStackTest.groovy │ │ ├── TotalStack.groovy │ │ └── TotalStackTest.groovy │ └── resources │ └── lift.properties ├── sandbox ├── build.gradle └── src │ ├── main │ └── groovy │ │ └── com │ │ └── github │ │ └── mperry │ │ ├── A.groovy │ │ └── B.groovy │ └── test │ └── groovy │ └── com │ └── github │ └── mperry │ └── TestCoercion.groovy ├── settings.gradle ├── test-extensions ├── build.gradle └── src │ └── test │ └── groovy │ └── com │ └── github │ └── mperry │ └── fg │ ├── ListMonadExtensionTest.groovy │ ├── OptionMonadExtensionTest.groovy │ ├── SetMonadExtensionTest.groovy │ └── test │ └── ListJavaExtensionTest.groovy └── typeclass ├── build.gradle └── src ├── main └── groovy │ └── com │ └── github │ └── mperry │ └── fg │ └── typeclass │ ├── Applicative.groovy │ ├── Comonad.groovy │ ├── Functor.groovy │ ├── Monad.groovy │ ├── MonadPlus.groovy │ ├── MonadTrans.groovy │ ├── Monoid.groovy │ ├── Semigroup.groovy │ └── concrete │ ├── IOMonad.groovy │ ├── ListApplicative.groovy │ ├── ListFunctor.groovy │ ├── ListMonad.groovy │ ├── OptionApplicative.groovy │ ├── OptionFunctor.groovy │ ├── OptionMonad.groovy │ ├── OptionMonadTrans.groovy │ ├── OptionT.groovy │ ├── SafeIOMonad.groovy │ ├── SetMonad.groovy │ ├── StateInt.groovy │ └── StateIntMonad.groovy └── test └── groovy └── com └── github └── mperry └── fg └── typeclass ├── FunctorTest.groovy ├── IOMonadTest.groovy ├── ListApplicativeTest.groovy ├── ListFunctorTest.groovy ├── MonadTransTest.groovy ├── OptionApplicativeTest.groovy └── OptionMonadTest.groovy /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | text eol=lf 4 | 5 | # Custom for Visual Studio 6 | *.cs diff=csharp 7 | *.sln merge=union 8 | *.csproj merge=union 9 | *.vbproj merge=union 10 | *.fsproj merge=union 11 | *.dbproj merge=union 12 | 13 | # Standard to msysgit 14 | *.doc diff=astextplain 15 | *.DOC diff=astextplain 16 | *.docx diff=astextplain 17 | *.DOCX diff=astextplain 18 | *.dot diff=astextplain 19 | *.DOT diff=astextplain 20 | *.pdf diff=astextplain 21 | *.PDF diff=astextplain 22 | *.rtf diff=astextplain 23 | *.RTF diff=astextplain 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | #.gitignore 3 | .gradle 4 | classes 5 | workspace.xml 6 | .idea 7 | *.iml 8 | out 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | 2 | # http://lint.travis-ci.org/ 3 | language: groovy 4 | 5 | sudo: false 6 | 7 | jdk: 8 | - oraclejdk8 9 | 10 | env: 11 | - secure: "Xv6ITZIEX+0wrev44Tzkg76oIzVYK2uOTb+Iubx/syhi9pyeA8MXhg6np7rV62bEs0KsXSygjdlYPlJKx5Gzq4e7MZQ/TZjC5Niu37tpRrI5Gc2f33I/VlYWvvKKoQzGgNf13oOAWHt84Vchk6zdJINLq71nRk+921fO7Ssl3VE=" 12 | - secure: "tXjIV6JUt+1gKfL0qYzyY4jzbJG6kk32f7RmQn3vYmJXLxgNBnCDkOQ9GkJuiBld8GG2nsvAY4m34LmaxJzKzA/bZNzdkT7427ozAaGsQSRHHyc+/2UGOw/T3K3zF02h0W++yVTNHWuzXHCkitAlsw7esp9u97FdhqP08vzMLC0=" 13 | 14 | 15 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Functional Groovy 2 | ================= 3 | 4 | [Functional Groovy](https://github.com/mperry/functionalgroovy) is a library for doing functional programming 5 | (FP) in Groovy. It is a Groovy extension module for [Functional Java](https://functionaljava.github.io/) (FJ), adding Groovy idioms and new FP constructs in Groovy. 6 | 7 | Features includes: 8 | * FunctionalJava based 9 | * Enhances FunctionalJava for Groovy as a Groovy extension module 10 | * Groovy Quickcheck style property testing (specification based testing) 11 | * Monad library using a minimal monad implementation (unit/flatMap) 12 | * Monadic functions added to the standard Java List 13 | * Monad comprehensions (dynamically typed) 14 | * Lenses 15 | * Reader, Writer and State monads 16 | * A simple IO type 17 | * Y Combinator 18 | 19 | The project has some cloud build servers I used to experiment with including: 20 | * [Cloudbees Jenkins Build](https://mperry.ci.cloudbees.com/job/functionalgroovy/) 21 | * [Sonatype Functional Groovy Artifacts](https://oss.sonatype.org/content/groups/public/com/github/mperry/) 22 | * [Travis CI Build](https://travis-ci.org/mperry/functionalgroovy/builds) 23 | 24 | I have written an initial blog post on [Groovy Null Handling using Bind, Comprehensions and Lift](http://mperry.github.io/2013/07/28/groovy-null-handling.html) covering: 25 | * some introductory material on functional programming in Groovy 26 | * how to begin using the Functional Groovy library 27 | * handling `null`s by binding through the `Option` type, monadic comprehensions and monadic lifting 28 | 29 | The full list of related posts are: 30 | * [Groovy Null Handling Using Bind, Comprehensions and Lift](http://mperry.github.io/2013/07/28/groovy-null-handling.html) 31 | * [Specification Based Testing](http://mperry.github.io/2013/12/09/specification-based-testing.html) 32 | * [Referentially Transparent Input/Output in Groovy](http://mperry.github.io/2014/01/03/referentially-transparent-io.html) 33 | * [Folds and Unfolds](http://mperry.github.io/2014/01/21/folds-and-unfolds.html) 34 | 35 | To start using the library add the dependency `com.github.mperry:functionalgroovy-main:0.5.1-SNAPSHOT` to your Gradle 36 | project. A simple test script to get going (`test.groovy`) is: 37 | 38 | ```groovy 39 | @GrabResolver('https://oss.sonatype.org/content/groups/public') 40 | @Grab('com.github.mperry:functionalgroovy-core:0.5.1-SNAPSHOT') 41 | @Grab('org.functionaljava:functionaljava:4.1') 42 | 43 | import com.github.mperry.fg.* 44 | 45 | 1.to(5).each { 46 | println it 47 | } 48 | ``` 49 | 50 | Run this script using `groovy test.groovy`. 51 | 52 | This project uses: 53 | * JDK 8 54 | * Gradle 1.11 55 | * Groovy 2.3.2 56 | * Functional Java 4.1 57 | * Intellij Community Edition 13.1 58 | 59 | I have added a list of [open issues](https://github.com/mperry/functionalgroovy/issues?state=open) so feel free to 60 | contribute. Some ways of contributing are: 61 | * adding new functionality 62 | * adding tests 63 | * adding FP in Groovy examples 64 | * adding FunctionalGroovy usage examples 65 | 66 | Functional Groovy is divided into four components: core, main, demo, java8 and consume. 67 | * Core enhances Functional Java with Groovy idioms 68 | * Main adds new functionality 69 | * Demo includes examples of FP in Groovy and usage of this library 70 | * Java8 includes enhancements related to Java 8 types (e.g. Optional). 71 | * Consume shows how to include FunctionalGroovy in your project 72 | -------------------------------------------------------------------------------- /base/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | ext { 3 | 4 | } 5 | 6 | uploadArchives.enabled = true 7 | 8 | dependencies { 9 | compile gradleGroovy 10 | compile gradleFj 11 | compile project(":core") 12 | 13 | testCompile gradleJunit 14 | } 15 | 16 | 17 | -------------------------------------------------------------------------------- /base/src/main/groovy/com/github/mperry/fg/YCombinator.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 23/01/14. 7 | */ 8 | //@TypeChecked 9 | class YCombinator { 10 | 11 | /** 12 | * Strict applicative order Y combinator 13 | * 14 | * (define Y 15 | * (lambda (f) 16 | * ((lambda (x) (x x)) 17 | * (lambda (x) (f (lambda (y) ((x x) y))))))) 18 | * 19 | * @param fx 20 | * @return 21 | */ 22 | static def Y(Closure f) { 23 | def h = { Closure x -> 24 | // y is the value passed in 25 | f { y -> 26 | x(x)(y) 27 | } 28 | } 29 | h(h) 30 | } 31 | 32 | static def Y2(def f) { 33 | f({ x -> Y2(f)(x)}) 34 | 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /base/src/test/groovy/com/github/mperry/fg/YCombinatorTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | import org.junit.Assert 5 | import org.junit.Test 6 | 7 | import static org.junit.Assert.assertTrue 8 | 9 | /** 10 | * Created by MarkPerry on 23/01/14. 11 | */ 12 | //@TypeChecked 13 | class YCombinatorTest { 14 | 15 | @Test 16 | void testFib() { 17 | Closure fib = { Closure f -> 18 | { int n -> 19 | n < 2 ? n : f(n - 1) + f(n - 2) 20 | } 21 | } 22 | def f = YCombinator.Y2(fib) 23 | def x = f(6) 24 | 25 | println x 26 | Assert.assertTrue(x == 8) 27 | } 28 | 29 | @Test 30 | void testFactorial() { 31 | def fact = { Closure f -> 32 | { int n -> 33 | n == 1 ? n : n * f(n - 1) 34 | } 35 | } 36 | def f = YCombinator.Y(fact) 37 | def z = f(4) 38 | println z 39 | assertTrue(z == 24) 40 | // f(100) 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /consume/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | repositories { 3 | 4 | mavenCentral() 5 | maven { 6 | url sonatypeRepositoryUrl 7 | } 8 | } 9 | 10 | ext { 11 | 12 | } 13 | 14 | dependencies { 15 | compile gradleGroovy 16 | compile gradleFj 17 | compile "$groupName:$projectName-main:$consumeVersion" 18 | testCompile gradleJunit 19 | } 20 | -------------------------------------------------------------------------------- /consume/scripts/bugImplicitBoxing.groovy: -------------------------------------------------------------------------------- 1 | 2 | @GrabResolver('https://oss.sonatype.org/content/repositories/snapshots/') 3 | @Grab('com.github.mperry:functionalgroovy-main:0.5-SNAPSHOT') 4 | 5 | import groovy.transform.TypeChecked 6 | import org.junit.Test 7 | 8 | import static junit.framework.Assert.assertTrue 9 | 10 | @TypeChecked 11 | class StreamTest { 12 | 13 | @Test 14 | void explicitBox() { 15 | assertTrue(new Integer(1).to(3).toJavaList() == (1..3).toList()) 16 | } 17 | 18 | @Test 19 | void implicitBox() { 20 | // fails with error: 21 | 22 | /* 23 | Caught: BUG! exception in phase 'instruction selection' in source unit 'D:\repositories\functionalgroovy\consume\scripts\stream.groovy' Declaring class for method call to 'fj.data.Stream to(java.lang.Integer, java.lang.Integer)' declared in java.lang.Integer was not matched with found receiver int. This should not have happened! 24 | 25 | BUG! exception in phase 'instruction selection' in source unit 'D:\repositories\functionalgroovy\consume\scripts\stream.groovy' Declaring class for method call to 'fj.data.Stream to(java.lang.Integer, java.lang.Integer)' declared in java.lang.Integer was not matched with found receiver int. This should not have happened! 26 | 27 | */ 28 | 29 | // comment out this test to demonstrate that the explicit boxing test 30 | // above works 31 | assertTrue(1.to(3).toJavaList() == (1..3).toList()) 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /consume/scripts/exceptionHandling.groovy: -------------------------------------------------------------------------------- 1 | @GrabResolver(name='custom', root='https://oss.sonatype.org/content/groups/public', m2Compatible=true) 2 | //@Grab('org.functionaljava:functionaljava:4.2-SNAPSHOT') 3 | @Grab('com.github.mperry:functionalgroovy-core:0.6-SNAPSHOT') 4 | 5 | import fj.* 6 | import fj.data.* 7 | import groovy.transform.TypeChecked 8 | import org.junit.Test 9 | 10 | /** 11 | * Created by mperry on 4/08/2014. 12 | */ 13 | @TypeChecked 14 | class ExceptionHandling { 15 | 16 | Validation contents(String s) { 17 | ({ -> (new URL(s)).text } as P1).validate() 18 | } 19 | 20 | Validation contents2(String s) { 21 | P.lazy({ -> (new URL(s)).text } as P1).validate() 22 | } 23 | 24 | @Test 25 | void errorTest() { 26 | def actual = ["malformed", "http://www.this-page-intentionally-left-blank.org/"].collect { String s -> 27 | contents2(s).isSuccess() 28 | } 29 | assert actual == [false, true] 30 | } 31 | 32 | } 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /consume/scripts/genericTypeCheck.groovy: -------------------------------------------------------------------------------- 1 | 2 | // does not type check with Groovy 2.3.0-rc-4 3 | 4 | import groovy.transform.TypeChecked 5 | //import org.junit.Test 6 | 7 | @TypeChecked 8 | class Class1 { 9 | 10 | static void method1(A a, B b) { 11 | method2(a, b) 12 | } 13 | 14 | static void method2(A a, B b) { 15 | } 16 | 17 | static void method3(List list1, List list2) { 18 | method1(list1.get(0), list2.get(0)) 19 | } 20 | 21 | } 22 | 23 | -------------------------------------------------------------------------------- /consume/scripts/optionMonadTest.groovy: -------------------------------------------------------------------------------- 1 | 2 | 3 | // from https://github.com/mperry/functionalgroovy/blob/master/test-extensions/src/test/groovy/com/github/mperry/fg/OptionMonadExtensionTest.groovy 4 | 5 | //@GrabResolver('https://oss.sonatype.org/content/groups/public') 6 | @GrabResolver('https://oss.sonatype.org/content/repositories/snapshots/') 7 | @Grab('com.github.mperry:functionalgroovy-main:0.5-SNAPSHOT') 8 | import fj.F 9 | import fj.F2 10 | import fj.function.Integers 11 | import groovy.transform.TypeChecked 12 | import org.junit.Test 13 | import com.github.mperry.fg.typeclass.concrete.OptionMonad 14 | 15 | import static fj.Function.uncurryF2 16 | import static fj.data.Option.none 17 | import static fj.data.Option.some 18 | import static junit.framework.Assert.assertTrue 19 | 20 | @TypeChecked 21 | class OptionMonadTest { 22 | 23 | OptionMonad monad() { 24 | new OptionMonad() 25 | } 26 | 27 | @Test 28 | void ap() { 29 | def f = { Integer i -> i + 1} as F 30 | assertTrue(monad().ap(some(1), some(f)) == some(2)) 31 | assertTrue(monad().ap(some(1), none()) == none()) 32 | assertTrue(monad().ap(none(), some(f)) == none()) 33 | } 34 | 35 | @Test 36 | void filterM() { 37 | def f = { Integer i -> some(i > 0)} as F 38 | def actual = monad().filterM([2, 1, 0, -1], f) 39 | assertTrue(actual == some([2, 1])) 40 | } 41 | 42 | @Test 43 | void fmap() { 44 | def actual = monad().fmap(Integers.negate, some(1)) 45 | assertTrue(actual == some(-1)) 46 | } 47 | 48 | @Test 49 | void foldM() { 50 | def saveDiv = { BigDecimal n, Integer d -> d == 0 ? none() : some(n/d) } as F2 51 | assertTrue(monad().foldM([4, 5], 40.toBigDecimal(), saveDiv) == some(2.toBigDecimal())) 52 | assertTrue(monad().foldM([4, 0, 2], 40.toBigDecimal(), saveDiv) == none()) 53 | } 54 | 55 | @Test 56 | void join() { 57 | assertTrue(monad().join(some(some(1))) == some(1)) 58 | assertTrue(monad().join(some(none())) == none()) 59 | assertTrue(monad().join(none()) == none()) 60 | } 61 | 62 | @Test 63 | void liftM() { 64 | def f = Integers.negate 65 | assertTrue(monad().liftM(some(4), f) == some(-4)) 66 | assertTrue(monad().liftM(none(), f) == none()) 67 | } 68 | 69 | @Test 70 | void liftM2() { 71 | def f = uncurryF2(Integers.multiply) 72 | assertTrue(monad().liftM2(some(3), some(4), f) == some(12)) 73 | assertTrue(monad().liftM2(some(3), none(), f) == none()) 74 | assertTrue(monad().liftM2(none(), none(), f) == none()) 75 | } 76 | 77 | @Test 78 | void sequence() { 79 | // println actual 80 | assertTrue(monad().sequence([some(2), some(3)]) == some([2, 3])) 81 | assertTrue(monad().sequence([some(2), none()]) == none()) 82 | } 83 | 84 | @Test 85 | void replicateM() { 86 | assertTrue(monad().replicateM(4, some(3)) == some([3, 3, 3, 3])) 87 | assertTrue(monad().replicateM(3, none()) == none()) 88 | } 89 | 90 | @Test 91 | void traverse() { 92 | def f = { Integer i -> i > 0 ? some(i) : none() } as F 93 | assertTrue(monad().traverse([2, -4, 6], f) == none()) 94 | assertTrue(monad().traverse([2, 6], f) == some([2, 6])) 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /consume/scripts/test.groovy: -------------------------------------------------------------------------------- 1 | 2 | @GrabResolver('https://oss.sonatype.org/content/groups/public') 3 | @Grab('com.github.mperry:functionalgroovy-core:0.5-SNAPSHOT') 4 | @Grab('org.functionaljava:functionaljava:3.1') 5 | 6 | import com.github.mperry.fg.* 7 | 8 | 9 | 1.to(5).each { 10 | println it 11 | } 12 | -------------------------------------------------------------------------------- /consume/src/test/groovy/com/github/mperry/fg/Example.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import groovy.transform.TypeChecked 5 | import org.junit.Test 6 | 7 | /** 8 | * Created with IntelliJ IDEA. 9 | * User: MarkPerry 10 | * Date: 13/12/13 11 | * Time: 4:12 PM 12 | * To change this template use File | Settings | File Templates. 13 | */ 14 | @TypeChecked 15 | class Example { 16 | 17 | @Test 18 | void test1() { 19 | println "First FunctionalGroovy demo" 20 | 1.to(5).each { 21 | println it 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /consume/src/test/groovy/com/github/mperry/fg/FutureTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.P1 4 | import fj.control.parallel.Promise 5 | import fj.control.parallel.Strategy 6 | import fj.data.Option 7 | import groovy.transform.TypeChecked 8 | import org.junit.Test 9 | 10 | import java.util.concurrent.Callable 11 | import java.util.concurrent.Executors 12 | import java.util.concurrent.Future 13 | import java.util.concurrent.FutureTask 14 | 15 | import static com.github.mperry.fg.test.Specification.specAssert 16 | import static org.junit.Assert.assertTrue 17 | 18 | /** 19 | * Created by MarkPerry on 17/04/2014. 20 | */ 21 | 22 | @TypeChecked 23 | class FutureTest { 24 | 25 | Future future(String s) { 26 | Executors.newCachedThreadPool().submit(callable(s)) 27 | } 28 | 29 | Callable callable(final String s) { 30 | new Callable() { 31 | @Override 32 | String call() throws Exception { 33 | Thread.sleep(100) // milliseconds 34 | s 35 | } 36 | } 37 | } 38 | 39 | Future task(final String s) { 40 | new FutureTask(callable(s)) 41 | } 42 | 43 | P1> transform(String val) { 44 | def p = Strategy.obtain(future(val)) 45 | p.map { String s -> Option.parseInt.f(s) } 46 | } 47 | 48 | Promise transform2(String val) { 49 | def p = Strategy.obtain(future(val)) 50 | def p2 = promise(p) 51 | p2 52 | } 53 | 54 | def Promise promise(P1 p) { 55 | def promise = Promise.promise(Strategy.simpleThreadStrategy(), p) 56 | promise 57 | } 58 | 59 | Boolean truth(String s) { 60 | // mapping the product of the future equals calling the function directly 61 | transform(s)._1() == Option.parseInt.f(s) 62 | } 63 | 64 | Boolean truth2(String s) { 65 | def a = transform2(s).fmap({ String str -> Option.parseInt.f(str)}).claim() 66 | def b = Option.parseInt.f(s) 67 | a == b 68 | } 69 | 70 | @Test 71 | void three() { 72 | assertTrue(truth("3")) 73 | } 74 | 75 | @Test 76 | void simple() { 77 | ["not an int", "3", " 3", "3 "].each { String s -> 78 | def b = truth(s) 79 | // println("s: $s, b: $b") 80 | assertTrue(b) 81 | } 82 | } 83 | 84 | @Test 85 | void simple2() { 86 | ["not an int", "3", " 3", "3 "].each { String s -> 87 | def b = truth2(s) 88 | println("s: $s, b: $b") 89 | assertTrue(b) 90 | } 91 | } 92 | 93 | @Test 94 | void spec() { 95 | specAssert { String s -> truth(s) } 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /core/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | import org.apache.tools.ant.filters.* 3 | 4 | ext { 5 | } 6 | 7 | dependencies { 8 | compile gradleGroovy 9 | compile gradleFj 10 | compile gradleFj 11 | compile gradleFjQuickcheck 12 | compile gradleJunit 13 | } 14 | 15 | uploadArchives.enabled = true 16 | 17 | processResources { 18 | doFirst { 19 | filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: [version: project.version]) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/ClassExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 15/01/14. 7 | */ 8 | @TypeChecked 9 | class ClassExtension { 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/CollectionExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F2 5 | import fj.data.Option 6 | import groovy.transform.TypeChecked 7 | import groovy.transform.TypeCheckingMode 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * User: MarkPerry 12 | * Date: 12/06/13 13 | * Time: 11:27 PM 14 | * To change this template use File | Settings | File Templates. 15 | */ 16 | @TypeChecked 17 | class CollectionExtension { 18 | 19 | static List map(Collection collection, F f) { 20 | collection.collect(FExtension.toClosure(f)) 21 | } 22 | 23 | static Collection filter(Collection collection, F f) { 24 | collection.findAll(FExtension.toClosure(f)) 25 | } 26 | 27 | static B fold(Collection collection, B initial, F2 f) { 28 | collection.inject(initial, { B b, A a -> f.f(a, b)}) 29 | } 30 | 31 | static List flatMap(Collection c, F> f) { 32 | c.collectMany(FExtension.toClosure(f)) 33 | } 34 | 35 | static Boolean exists(Collection c, F f) { 36 | findFirst(c, f).isSome() 37 | } 38 | 39 | static Boolean forAll(Collection c, F f) { 40 | !exists(c, { A a -> f.f(a) == false }) 41 | } 42 | 43 | static Option findFirst(Collection c, F f) { 44 | Option.fromNull(c.find(FExtension.toClosure(f))) 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/EitherExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.data.Option 4 | import fj.data.Either 5 | import groovy.transform.TypeChecked 6 | import groovy.transform.TypeCheckingMode 7 | 8 | /** 9 | * Created with IntelliJ IDEA. 10 | * User: PerryMa 11 | * Date: 30/11/12 12 | * Time: 1:45 PM 13 | * To change this template use File | Settings | File Templates. 14 | */ 15 | @TypeChecked 16 | class EitherExtension { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/F2Extension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F2 5 | import fj.P1 6 | import groovy.transform.TypeChecked 7 | 8 | /** 9 | * Created with IntelliJ IDEA. 10 | * User: MarkPerry 11 | * Date: 1/12/13 12 | * Time: 3:23 PM 13 | * To change this template use File | Settings | File Templates. 14 | */ 15 | @TypeChecked 16 | class F2Extension { 17 | 18 | static Closure toClosure(F2 f) { 19 | { A a, B b -> 20 | f.f(a, b) 21 | } 22 | } 23 | 24 | // static P1 f_(F2 f, A a, B b) { 25 | // { -> f.f(a, b) } as P1 26 | // } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/F2StaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: MarkPerry 8 | * Date: 20/12/13 9 | * Time: 8:46 AM 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | @TypeChecked 13 | class F2StaticExtension { 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/F3Extension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F2 5 | import fj.F3 6 | import fj.F4 7 | import fj.P1 8 | import groovy.transform.TypeChecked 9 | 10 | /** 11 | * Created with IntelliJ IDEA. 12 | * User: MarkPerry 13 | * Date: 22/11/13 14 | * Time: 10:08 PM 15 | * To change this template use File | Settings | File Templates. 16 | */ 17 | @TypeChecked 18 | class F3Extension { 19 | 20 | static Closure toClosure(F3 f) { 21 | { A a, B b, C c -> 22 | f.f(a, b, c) 23 | } 24 | } 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/F3StaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: MarkPerry 8 | * Date: 20/12/13 9 | * Time: 8:46 AM 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | @TypeChecked 13 | class F3StaticExtension { 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/F4Extension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F3 5 | import fj.F4 6 | import fj.F5 7 | import fj.P1 8 | import groovy.transform.TypeChecked 9 | 10 | /** 11 | * Created with IntelliJ IDEA. 12 | * User: MarkPerry 13 | * Date: 22/11/13 14 | * Time: 10:08 PM 15 | * To change this template use File | Settings | File Templates. 16 | */ 17 | @TypeChecked 18 | class F4Extension { 19 | 20 | static Closure toClosure(F4 f) { 21 | { A a, B b, C c, D d -> 22 | f.f(a, b, c, d) 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/F4StaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: MarkPerry 8 | * Date: 20/12/13 9 | * Time: 8:46 AM 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | @TypeChecked 13 | class F4StaticExtension { 14 | } 15 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/F5Extension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F4 5 | import fj.F5 6 | import fj.P1 7 | import groovy.transform.TypeChecked 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * User: MarkPerry 12 | * Date: 22/11/13 13 | * Time: 9:47 PM 14 | * To change this template use File | Settings | File Templates. 15 | */ 16 | @TypeChecked 17 | class F5Extension { 18 | 19 | static Closure<$F> toClosure(F5 f) { 20 | { A a, B b, C c, D d, E e -> 21 | f.f(a, b, c, d, e) 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/F5StaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 7/01/14. 7 | */ 8 | @TypeChecked 9 | class F5StaticExtension { 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/F6Extension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F5 5 | import fj.F6 6 | import fj.P1 7 | import groovy.transform.TypeChecked 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * User: MarkPerry 12 | * Date: 22/11/13 13 | * Time: 10:08 PM 14 | * To change this template use File | Settings | File Templates. 15 | */ 16 | @TypeChecked 17 | class F6Extension { 18 | 19 | static Closure toClosure(F6 func) { 20 | { A a, B b, C c, D d, E e, $F f -> 21 | func.f(a, b, c, d, e, f) 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/F6StaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 7/01/14. 7 | */ 8 | @TypeChecked 9 | class F6StaticExtension { 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/F7Extension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F6 5 | import fj.F7 6 | import fj.P1 7 | import groovy.transform.TypeChecked 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * User: MarkPerry 12 | * Date: 22/11/13 13 | * Time: 10:08 PM 14 | * To change this template use File | Settings | File Templates. 15 | */ 16 | @TypeChecked 17 | class F7Extension { 18 | 19 | static Closure toClosure(F7 func) { 20 | { A a, B b, C c, D d, E e, $F f, G g -> 21 | func.f(a, b, c, d, e, f, g) 22 | } 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/F7StaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 7/01/14. 7 | */ 8 | @TypeChecked 9 | class F7StaticExtension { 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/F8Extension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F7 5 | import fj.F8 6 | import fj.P1 7 | import groovy.transform.TypeChecked 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * User: MarkPerry 12 | * Date: 22/11/13 13 | * Time: 10:08 PM 14 | * To change this template use File | Settings | File Templates. 15 | */ 16 | @TypeChecked 17 | class F8Extension { 18 | 19 | 20 | static Closure toClosure(F8 func) { 21 | { A a, B b, C c, D d, E e, $F f, G g, H h -> 22 | func.f(a, b, c, d, e, f, g, h) 23 | } 24 | } 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/F8StaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 7/01/14. 7 | */ 8 | @TypeChecked 9 | class F8StaticExtension { 10 | } 11 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/FExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F3 5 | import fj.P1 6 | import groovy.transform.TypeChecked 7 | 8 | /** 9 | * Created with IntelliJ IDEA. 10 | * User: MarkPerry 11 | * Date: 1/12/13 12 | * Time: 3:23 PM 13 | * To change this template use File | Settings | File Templates. 14 | */ 15 | @TypeChecked 16 | class FExtension { 17 | 18 | static Closure toClosure(F f) { 19 | { A a -> f.f(a) } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/FStaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import groovy.transform.TypeChecked 5 | 6 | /** 7 | * Created with IntelliJ IDEA. 8 | * User: MarkPerry 9 | * Date: 20/12/13 10 | * Time: 8:45 AM 11 | * To change this template use File | Settings | File Templates. 12 | */ 13 | @TypeChecked 14 | class FStaticExtension { 15 | 16 | public static F unit(F z, Closure closure) { 17 | closure as F 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/IntegerExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.data.Either 4 | import fj.data.Stream 5 | import fj.data.Enumerator 6 | import groovy.transform.TypeChecked 7 | 8 | /** 9 | * Created with IntelliJ IDEA. 10 | * User: MarkPerry 11 | * Date: 19/12/12 12 | * Time: 11:20 PM 13 | * To change this template use File | Settings | File Templates. 14 | */ 15 | @TypeChecked 16 | class IntegerExtension { 17 | 18 | /** 19 | * @param from Start from (inclusive) 20 | * @param to End at (inclusive) 21 | * @return 22 | */ 23 | public static Stream to(Integer from, Integer to) { 24 | Stream.range(from, to + 1) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/ListFJExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: mwp 8 | * Date: 3/12/13 9 | * Time: 9:49 AM 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | @TypeChecked 13 | class ListFJExtension { 14 | 15 | static java.util.List toJavaList(fj.data.List list) { 16 | new ArrayList(list.toCollection()) 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/ListJavaStaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.P2 5 | import fj.data.Option 6 | import groovy.transform.TypeChecked 7 | import groovy.transform.TypeCheckingMode 8 | 9 | /** 10 | * Created by MarkPerry on 30/12/13. 11 | */ 12 | @TypeChecked 13 | class ListJavaStaticExtension { 14 | 15 | public static java.util.List repeat(List list, Integer n, A a) { 16 | def result = [] 17 | for (int i = 0; i < n; i++) { 18 | result.add(a) 19 | } 20 | result 21 | } 22 | 23 | /** 24 | * Similar to fj.data.Stream, takes constant stack size 25 | * @param list 26 | * @param b 27 | * @param f 28 | * @return 29 | */ 30 | static List unfold(List list, B b, F>> f) { 31 | def result = [] 32 | def val = b 33 | def loop = true 34 | while (loop) { 35 | def o = f.f(val) 36 | if (o.isNone()) { 37 | loop = false 38 | } else { 39 | def p = o.some() 40 | result.add(p._1()) 41 | val = p._2() 42 | } 43 | } 44 | result 45 | } 46 | 47 | static List unfold(List list, B b, Closure>> f) { 48 | unfold(list, b, f as F) 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/ListOps.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 26/06/2014. 7 | */ 8 | @TypeChecked 9 | class ListOps { 10 | 11 | static List plus(List list1, List list2) { 12 | def result = new ArrayList(list1) 13 | result.addAll(list2) 14 | result 15 | } 16 | 17 | static List plus(A a, List list) { 18 | def result = new ArrayList() 19 | result.add(a) 20 | result.addAll(list) 21 | result 22 | } 23 | 24 | static List plus(List list, A a) { 25 | def result = new ArrayList() 26 | result.addAll(list) 27 | result.add(a) 28 | result 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/ObjectCompanion.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 15/01/14. 7 | */ 8 | @TypeChecked 9 | class ObjectCompanion { 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/ObjectExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import groovy.transform.TypeChecked 5 | import groovy.transform.TypeCheckingMode 6 | 7 | /** 8 | * Created by MarkPerry on 15/01/14. 9 | */ 10 | @TypeChecked 11 | class ObjectExtension { 12 | 13 | static Boolean isSubInstanceOf(Object o, Class c) { 14 | c.isAssignableFrom(o.class) 15 | } 16 | 17 | static Boolean isDirectInstanceOf(Object o, Class c) { 18 | o.class == c 19 | } 20 | 21 | static Boolean isProperSubInstanceOf(Object o, Class c) { 22 | isSubInstanceOf(o, c) && !isDirectInstanceOf(o, c) 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/OptionExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.data.Option 4 | import fj.F 5 | import groovy.transform.TypeChecked 6 | import groovy.transform.TypeCheckingMode 7 | 8 | /** 9 | * Created with IntelliJ IDEA. 10 | * User: PerryMa 11 | * Date: 30/11/12 12 | * Time: 12:28 PM 13 | * To change this template use File | Settings | File Templates. 14 | */ 15 | @TypeChecked 16 | class OptionExtension { 17 | 18 | static Option flatMap(Option option, F> f) { 19 | option.bind(f) 20 | } 21 | 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/OptionStaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F2 5 | import fj.F3 6 | import fj.data.Option 7 | import groovy.transform.TypeChecked 8 | import groovy.transform.TypeCheckingMode 9 | 10 | /** 11 | * Created with IntelliJ IDEA. 12 | * User: PerryMa 13 | * Date: 30/11/12 14 | * Time: 12:40 PM 15 | * To change this template use File | Settings | File Templates. 16 | */ 17 | @TypeChecked 18 | class OptionStaticExtension { 19 | 20 | public static Option unit(Option option, A value) { 21 | Option.some(value) 22 | } 23 | 24 | public static F, Option> liftM1(Option o, F f) { 25 | { Option oa, Option ob -> 26 | oa.map({ A a -> 27 | f.f(a) 28 | } as F) 29 | } as F 30 | } 31 | 32 | public static F2, Option, Option> liftM2(Option o, F2 f) { 33 | { 34 | Option oa, Option ob -> 35 | oa.bind({ A a -> 36 | ob.map({ B b -> 37 | f.f(a, b) 38 | } as F) 39 | } as F) 40 | } as F2 41 | } 42 | 43 | public static F3, Option, Option, Option> liftM3(Option o, F3 f) { 44 | { 45 | Option oa, Option ob, Option oc -> 46 | oa.bind({ A a -> 47 | ob.bind({ B b -> 48 | oc.map({ C c -> 49 | f.f(a, b, c) 50 | } as F) 51 | } as F) 52 | } as F) 53 | } as F3 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/P1Extension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.P1 5 | import fj.data.Option 6 | import fj.data.Validation 7 | import groovy.transform.TypeChecked 8 | 9 | /** 10 | * Created by MarkPerry on 15/01/14. 11 | */ 12 | @TypeChecked 13 | class P1Extension { 14 | 15 | static Boolean throwsException(P1 p, Class error) { 16 | def v = validate(p) 17 | v.isFail() && ObjectExtension.isSubInstanceOf(v.fail(), error) 18 | } 19 | 20 | static Boolean throwsException(P1 p) { 21 | validate(p).isFail() 22 | } 23 | 24 | static Validation validate(P1 p) { 25 | try { 26 | Validation.success(p._1()) 27 | } catch (Exception e) { 28 | Validation.fail(e) 29 | } 30 | } 31 | 32 | static Boolean throwsError(P1 p, Class error) { 33 | def v = validateWithError(p) 34 | v.isFail() && ObjectExtension.isSubInstanceOf(v.fail(), error) 35 | } 36 | 37 | static Boolean throwsStackOverflow(P1 p) { 38 | throwsError(p, StackOverflowError.class) 39 | } 40 | 41 | static Boolean throwsError(P1 p) { 42 | validateWithError(p).isFail() 43 | } 44 | 45 | static Validation validateWithError(P1 p) { 46 | try { 47 | Validation.success(p._1()) 48 | } catch (Error e) { 49 | Validation.fail(e) 50 | } 51 | } 52 | 53 | 54 | } 55 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/Rand.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.P 4 | import fj.P2 5 | import groovy.transform.Canonical 6 | import groovy.transform.TypeChecked 7 | 8 | /** 9 | * Created by MarkPerry on 17/02/14. 10 | */ 11 | @TypeChecked 12 | @Canonical 13 | class Rand { 14 | 15 | Long seed 16 | 17 | P2 nextInt() { 18 | def newSeed = (seed * 0x5DEECE66DL + 0xBL) & 0xFFFFFFFFFFFFL 19 | def nextRng = new Rand(newSeed) 20 | def n = (Long) (newSeed >>> 16) 21 | def i = n.intValue() 22 | P.p(i, nextRng) 23 | } 24 | 25 | P2 nextNatural() { 26 | def p = nextInt() 27 | def i = p._1() 28 | P.p(i < 0 ? -(i + 1) : i, p._2()) 29 | } 30 | 31 | P2 nextDouble() { 32 | def p = nextNatural() 33 | P.p(p._1() / (Integer.MAX_VALUE.toDouble() + 1), p._2()) 34 | } 35 | 36 | P2 nextBoolean() { 37 | def p = nextInt() 38 | P.p(p._1() % 2 == 0, p._2()) 39 | } 40 | 41 | 42 | 43 | } 44 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/ShowExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: MarkPerry 8 | * Date: 9/08/13 9 | * Time: 2:04 AM 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | @TypeChecked 13 | class ShowExtension { 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/ShowStaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: MarkPerry 8 | * Date: 8/12/13 9 | * Time: 12:43 AM 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | @TypeChecked 13 | class ShowStaticExtension { 14 | 15 | 16 | // Show.anyShow 17 | // public static Show anyShowNullable(Show x) { 18 | // F> f = new F>() { 19 | // public Stream f(final A a) { 20 | // return Stream.fromString((a == null) ? "null" : a.toString()); 21 | // } 22 | // }; 23 | // return Show.show(f); 24 | // } 25 | 26 | 27 | } 28 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/ShowWorkaroundGroovy.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import com.github.mperry.ShowWorkaroundJava 4 | import fj.F 5 | import fj.Show 6 | import fj.test.Arg 7 | import groovy.transform.TypeChecked 8 | 9 | import static fj.Show.showS 10 | 11 | /** 12 | * Created with IntelliJ IDEA. 13 | * User: MarkPerry 14 | * Date: 8/12/13 15 | * Time: 10:24 PM 16 | * To change this template use File | Settings | File Templates. 17 | */ 18 | @TypeChecked 19 | class ShowWorkaroundGroovy { 20 | 21 | // Arg.argShow 22 | public static final Show> argShowNullable() { 23 | return showS(new F, String>() { 24 | public String f(final Arg arg) { 25 | return ShowWorkaroundJava.anyShowNullable().showS(arg.value()) + 26 | (arg.shrinks() > 0 ? " (" + arg.shrinks() + " shrink" + (arg.shrinks() == 1 ? "" : 's') + ')' : ""); 27 | } 28 | }); 29 | } 30 | 31 | // Show.anyShow 32 | // does not compile with: "Groovyc unable to resolve class A" 33 | // public static Show anyShowNullable() { 34 | // F> f = new F>() { 35 | // public Stream f(final A a) { 36 | // return Stream.fromString((a == null) ? "null" : a.toString()); 37 | // } 38 | // }; 39 | // return Show.show(f); 40 | // } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/StreamExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.data.Stream 4 | import fj.F 5 | import groovy.transform.TypeChecked 6 | import fj.F2 7 | import groovy.transform.TypeCheckingMode 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * User: MarkPerry 12 | * Date: 16/02/13 13 | * Time: 9:11 PM 14 | * To change this template use File | Settings | File Templates. 15 | */ 16 | @TypeChecked 17 | class StreamExtension { 18 | 19 | static Stream flatMap(Stream s, F> f) { 20 | s.bind(f) 21 | } 22 | 23 | static List toJList(Stream s) { 24 | s.map { Object it -> 25 | def isRecursive = Stream.isInstance(it) 26 | if (isRecursive) { 27 | toJList((Stream) it) 28 | } else { 29 | it 30 | } 31 | }.toCollection().toList() 32 | } 33 | 34 | static List toJavaList(Stream s) { 35 | new ArrayList(s.toCollection()) 36 | } 37 | 38 | static Stream combos(Stream s1, Stream s2) { 39 | s1.bind { A s1Val -> 40 | s2.map { A s2Val -> 41 | if (Stream.isInstance(s1Val)) { 42 | Stream s1Stream = (Stream) s1Val 43 | s1Stream.append(Stream.stream(s2Val)) 44 | } else { 45 | Stream.stream(s1Val, s2Val) 46 | } 47 | } 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/test/ArbitraryCompanion.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test 2 | 3 | import com.github.mperry.fg.ListFJExtension 4 | import fj.F 5 | import fj.test.Arbitrary 6 | import fj.test.Coarbitrary 7 | import fj.test.Gen 8 | import groovy.transform.TypeChecked 9 | import groovy.transform.TypeCheckingMode 10 | 11 | import static fj.test.Arbitrary.arbitrary 12 | 13 | /** 14 | * Created by MarkPerry on 10/01/14. 15 | */ 16 | @TypeChecked 17 | class ArbitraryCompanion { 18 | 19 | static Arbitrary> arbJavaList(Arbitrary aa) { 20 | def g = gen(aa.gen) 21 | arbitrary(g) 22 | } 23 | 24 | static Gen> gen(Gen gen) { 25 | Gen.listOf(gen).map({ fj.data.List list -> 26 | ListFJExtension.toJavaList(list) 27 | } as F) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/test/ArbitraryExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test 2 | 3 | import fj.test.Arbitrary 4 | import fj.test.Gen 5 | import groovy.transform.TypeChecked 6 | 7 | /** 8 | * Created with IntelliJ IDEA. 9 | * User: MarkPerry 10 | * Date: 30/11/13 11 | * Time: 9:28 PM 12 | * To change this template use File | Settings | File Templates. 13 | */ 14 | @TypeChecked 15 | class ArbitraryExtension { 16 | 17 | } 18 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/test/ArbitraryStaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test 2 | 3 | import fj.test.Arbitrary 4 | import fj.test.Gen 5 | import groovy.transform.TypeChecked 6 | 7 | /** 8 | * Created with IntelliJ IDEA. 9 | * User: MarkPerry 10 | * Date: 30/11/13 11 | * Time: 9:33 PM 12 | * To change this template use File | Settings | File Templates. 13 | */ 14 | @TypeChecked 15 | class ArbitraryStaticExtension { 16 | 17 | static Arbitrary arbNullableInteger(Arbitrary a) { 18 | def list = fj.data.List.list(Gen.value(null), Arbitrary.arbInteger.gen) 19 | Arbitrary.arbitrary(Gen.oneOf(list)) 20 | } 21 | 22 | static Arbitrary> arbJavaList(Arbitrary a, Arbitrary aa) { 23 | ArbitraryCompanion.arbJavaList(aa) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/test/ArgExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test 2 | 3 | import fj.test.Arg 4 | import groovy.transform.TypeChecked 5 | 6 | /** 7 | * Created with IntelliJ IDEA. 8 | * User: MarkPerry 9 | * Date: 9/08/13 10 | * Time: 2:02 AM 11 | * To change this template use File | Settings | File Templates. 12 | */ 13 | @TypeChecked 14 | class ArgExtension { 15 | 16 | } 17 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/test/ArgStaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: MarkPerry 8 | * Date: 8/12/13 9 | * Time: 1:48 AM 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | @TypeChecked 13 | class ArgStaticExtension { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/test/CheckResultCompanion.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test 2 | 3 | import com.github.mperry.ShowWorkaroundJava 4 | import fj.Show 5 | import fj.test.CheckResult 6 | import groovy.transform.TypeChecked 7 | 8 | /** 9 | * Created by MarkPerry on 31/01/14. 10 | */ 11 | @TypeChecked 12 | class CheckResultCompanion { 13 | 14 | static Show summaryNullable() { 15 | CheckResult.summary(ShowWorkaroundJava.argShowNullable()) 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/test/CheckResultExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test 2 | 3 | import fj.F 4 | import fj.Show 5 | import fj.Unit 6 | import fj.data.Stream 7 | import fj.test.Arg 8 | import fj.test.CheckResult 9 | import groovy.transform.TypeChecked 10 | import groovy.transform.TypeCheckingMode 11 | 12 | import static fj.Show.showS 13 | 14 | /** 15 | * Created with IntelliJ IDEA. 16 | * User: MarkPerry 17 | * Date: 9/08/13 18 | * Time: 12:49 AM 19 | * To change this template use File | Settings | File Templates. 20 | */ 21 | @TypeChecked 22 | class CheckResultExtension { 23 | 24 | static Unit printlnSummary(CheckResult cr) { 25 | CheckResult.summary.println(cr) 26 | } 27 | 28 | static Unit printlnSummaryNullable(CheckResult cr) { 29 | CheckResultCompanion.summaryNullable().println(cr) 30 | } 31 | 32 | static boolean isOk(CheckResult cr) { 33 | cr.isPassed() || cr.isProven() 34 | } 35 | 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/test/CheckResultStaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test 2 | 3 | import com.github.mperry.ShowWorkaroundJava 4 | import fj.Show 5 | import fj.test.CheckResult 6 | import groovy.transform.TypeChecked 7 | 8 | import static com.github.mperry.fg.test.ArgStaticExtension.argShowNullable 9 | 10 | /** 11 | * Created with IntelliJ IDEA. 12 | * User: MarkPerry 13 | * Date: 9/08/13 14 | * Time: 1:19 AM 15 | * To change this template use File | Settings | File Templates. 16 | */ 17 | @TypeChecked 18 | class CheckResultStaticExtension { 19 | 20 | static Show summaryNullable(CheckResult cr) { 21 | CheckResultCompanion.summaryNullable() 22 | // CheckResult.summary(ShowWorkaroundJava.argShowNullable()) 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/test/CoarbitraryCompanion.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test 2 | 3 | import com.github.mperry.fg.ListJavaExtension 4 | import fj.F 5 | import fj.test.Coarbitrary 6 | import groovy.transform.TypeChecked 7 | import groovy.transform.TypeCheckingMode 8 | 9 | /** 10 | * Created by MarkPerry on 10/01/14. 11 | */ 12 | @TypeChecked 13 | class CoarbitraryCompanion { 14 | 15 | static Coarbitrary> coarbJavaList(Coarbitrary ca) { 16 | Coarbitrary.coarbList(ca).compose({ java.util.List list -> ListJavaExtension.toFJList(list) } as F) 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/test/GenExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: MarkPerry 8 | * Date: 6/12/13 9 | * Time: 4:57 PM 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | @TypeChecked 13 | class GenExtension { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/test/GenStaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test 2 | 3 | import fj.F 4 | import fj.test.Arbitrary 5 | import fj.test.Gen 6 | import groovy.transform.TypeChecked 7 | 8 | /** 9 | * Created with IntelliJ IDEA. 10 | * User: MarkPerry 11 | * Date: 6/12/13 12 | * Time: 5:00 PM 13 | * To change this template use File | Settings | File Templates. 14 | */ 15 | @TypeChecked 16 | class GenStaticExtension { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /core/src/main/groovy/com/github/mperry/fg/test/PropertyExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test 2 | 3 | import fj.Show 4 | import fj.Unit 5 | import fj.test.CheckResult 6 | import fj.test.Property 7 | import groovy.transform.TypeChecked 8 | import groovy.transform.TypeCheckingMode 9 | 10 | import static org.junit.Assert.assertTrue 11 | 12 | /** 13 | * Created with IntelliJ IDEA. 14 | * User: MarkPerry 15 | * Date: 9/08/13 16 | * Time: 1:46 AM 17 | * To change this template use File | Settings | File Templates. 18 | */ 19 | @TypeChecked 20 | class PropertyExtension { 21 | 22 | static CheckResult checkOkWithSummary(Property p) { 23 | checkBooleanWithNullableSummary(p, true) 24 | } 25 | 26 | static CheckResult checkOkWithNullableSummary(Property p) { 27 | checkBooleanWithNullableSummary(p, true) 28 | } 29 | 30 | static CheckResult checkBooleanWithNullableSummary(Property p, boolean b) { 31 | def cr = p.check() 32 | CheckResultCompanion.summaryNullable().println(cr) 33 | assertTrue(CheckResultExtension.isOk(cr) == b) 34 | cr 35 | } 36 | 37 | 38 | } 39 | -------------------------------------------------------------------------------- /core/src/main/java/com/github/mperry/ShowWorkaroundJava.java: -------------------------------------------------------------------------------- 1 | package com.github.mperry; 2 | 3 | //import com.github.mperry.fg.ShowStaticExtension; 4 | import fj.F; 5 | import fj.Show; 6 | import fj.data.Stream; 7 | import fj.test.Arg; 8 | 9 | import static fj.Show.showS; 10 | 11 | /** 12 | * Created with IntelliJ IDEA. 13 | * User: MarkPerry 14 | * Date: 8/12/13 15 | * Time: 2:43 AM 16 | * To change this template use File | Settings | File Templates. 17 | */ 18 | public class ShowWorkaroundJava { 19 | 20 | // Arg.argShow 21 | public static final Show> argShowNullable() { 22 | return showS(new F, String>() { 23 | public String f(final Arg arg) { 24 | return anyShowNullable().showS(arg.value()) + 25 | (arg.shrinks() > 0 ? " (" + arg.shrinks() + " shrink" + (arg.shrinks() == 1 ? "" : 's') + ')' : ""); 26 | } 27 | }); 28 | } 29 | 30 | // Show.anyShow 31 | public static Show anyShowNullable() { 32 | F> f = new F>() { 33 | public Stream f(final A a) { 34 | return Stream.fromString((a == null) ? "null" : a.toString()); 35 | } 36 | }; 37 | return Show.show(f); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /core/src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule: -------------------------------------------------------------------------------- 1 | moduleName=functionalgroovy-core 2 | moduleVersion=@version@ 3 | extensionClasses=com.github.mperry.fg.OptionExtension,com.github.mperry.fg.ShowExtension,com.github.mperry.fg.EitherExtension,com.github.mperry.fg.IntegerExtension,com.github.mperry.fg.ListJavaExtension,com.github.mperry.fg.ListFJExtension,com.github.mperry.fg.StreamExtension,,com.github.mperry.fg.CollectionExtension,com.github.mperry.fg.test.CheckResultExtension,com.github.mperry.fg.test.PropertyExtension,com.github.mperry.fg.test.ArgExtension,com.github.mperry.fg.FExtension,com.github.mperry.fg.F2Extension,com.github.mperry.fg.F3Extension,com.github.mperry.fg.F4Extension,com.github.mperry.fg.F5Extension,com.github.mperry.fg.F6Extension,com.github.mperry.fg.F7Extension,com.github.mperry.fg.F8Extension,com.github.mperry.fg.test.ArbitraryExtension,com.github.mperry.fg.test.GenExtension,com.github.mperry.fg.ObjectExtension,com.github.mperry.fg.ClassExtension,com.github.mperry.fg.P1Extension 4 | staticExtensionClasses=com.github.mperry.fg.OptionStaticExtension,com.github.mperry.fg.ShowStaticExtension,com.github.mperry.fg.test.CheckResultStaticExtension,com.github.mperry.fg.test.ArbitraryStaticExtension,com.github.mperry.fg.test.GenStaticExtension,com.github.mperry.fg.test.ArgStaticExtension,com.github.mperry.fg.FStaticExtension,com.github.mperry.fg.F2StaticExtension,com.github.mperry.fg.F3StaticExtension,com.github.mperry.fg.F4StaticExtension,com.github.mperry.fg.F5StaticExtension,com.github.mperry.fg.F6StaticExtension,com.github.mperry.fg.F7StaticExtension,com.github.mperry.fg.F8StaticExtension,com.github.mperry.fg.ListJavaStaticExtension 5 | -------------------------------------------------------------------------------- /core/src/test/groovy/com/github/mperry/fg/RandTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.P2 4 | import groovy.transform.TypeChecked 5 | import org.junit.Test 6 | 7 | /** 8 | * Created by MarkPerry on 17/02/14. 9 | */ 10 | @TypeChecked 11 | class RandTest { 12 | 13 | @Test 14 | void test1() { 15 | def r = new Rand(42L) 16 | def p = r.nextInt() 17 | println(p) 18 | def p2 = p._2().nextInt() 19 | println(p2) 20 | 21 | } 22 | 23 | def void println(P2 p) { 24 | println("<${p._1()}, ${p._2()}>") 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /demo/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | apply plugin: 'application' 3 | 4 | mainClassName = "com.github.mperry.fg.SimpleIODemoFunctional" 5 | 6 | ext { 7 | } 8 | 9 | run { 10 | standardInput = System.in 11 | } 12 | 13 | dependencies { 14 | compile project(":main") 15 | compile gradleGroovy 16 | testCompile gradleJunit 17 | } 18 | -------------------------------------------------------------------------------- /demo/src/main/groovy/com/github/mperry/fg/ExceptionHandling.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.P1 4 | import fj.data.Validation 5 | import groovy.transform.TypeChecked 6 | import org.junit.Test 7 | 8 | /** 9 | * Created by mperry on 4/08/2014. 10 | */ 11 | @TypeChecked 12 | class ExceptionHandling { 13 | 14 | Validation contents(String s) { 15 | ({ -> (new URL(s)).text } as P1).validate() 16 | } 17 | 18 | @Test 19 | void errorTest() { 20 | def actual = ["malformed", "http://www.this-page-intentionally-left-blank.org/"].collect { String s -> 21 | contents(s).isSuccess() 22 | } 23 | assert actual == [false, true] 24 | } 25 | 26 | } 27 | 28 | 29 | -------------------------------------------------------------------------------- /demo/src/main/groovy/com/github/mperry/fg/FileList.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.data.IO 4 | import groovy.transform.TypeChecked 5 | 6 | /** 7 | * Created by mperry on 4/08/2014. 8 | */ 9 | @TypeChecked 10 | class FileList { 11 | 12 | IO> ls(File f) { 13 | { -> f.listFiles() as List } as IO 14 | } 15 | 16 | IO size(File f) { 17 | { -> f.length() } as IO 18 | } 19 | 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /demo/src/main/groovy/com/github/mperry/fg/Keno.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 13/01/14. 7 | */ 8 | @TypeChecked 9 | class Keno { 10 | } 11 | -------------------------------------------------------------------------------- /demo/src/main/groovy/com/github/mperry/fg/MyTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import org.junit.Test 4 | 5 | /** 6 | * Created by MarkPerry on 14/02/14. 7 | */ 8 | class MyTest { 9 | 10 | @Test 11 | void test1() { 12 | 13 | // def a = if (true) { "dasfdf" } 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /demo/src/main/groovy/com/github/mperry/fg/PaperScissorsRock.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: MarkPerry 6 | * Date: 22/11/13 7 | * Time: 12:51 PM 8 | * To change this template use File | Settings | File Templates. 9 | */ 10 | 11 | import fj.* 12 | import fj.data.* 13 | import fj.test.* 14 | import groovy.transform.TypeChecked 15 | import groovy.transform.TypeCheckingMode 16 | 17 | @TypeChecked 18 | class PaperScissorsRock { 19 | 20 | enum Move { PAPER, SCISSORS, ROCK } 21 | enum MoveResult { WIN, LOSE, DRAW } 22 | 23 | static void main(def args) { 24 | new PaperScissorsRock().gameSequence() 25 | } 26 | 27 | void gameSequence() { 28 | gameMessages().take(20).toList().each { 29 | println it 30 | } 31 | } 32 | 33 | Stream gameMessages() { 34 | messages().zipIndex().map { P2 p2 -> 35 | "game: ${p2._2()} ${p2._1()}" 36 | } 37 | } 38 | 39 | Stream messages() { 40 | moves().zip(moves()).map { P2 p -> 41 | gameMessage(p._1(), p._2(), result(p._1(), p._2())) 42 | 43 | } 44 | } 45 | 46 | String gameMessage(Move m1, Move m2, MoveResult r) { 47 | "p1: $m1 p2: $m2 result: ${gameResultMessage(r, "p1 wins", "p2 wins", "draw")}" 48 | } 49 | 50 | String gameResultMessage(MoveResult r, String win, String lost, String draw) { 51 | r == MoveResult.DRAW ? draw : (r == MoveResult.WIN ? win : lost) 52 | } 53 | 54 | @TypeChecked(TypeCheckingMode.SKIP) 55 | Stream moves() { 56 | Stream.range(1).map { 57 | randomMove(new Random())._2() 58 | } 59 | } 60 | 61 | P2 randomMove(Random r) { 62 | def v = Move.values() 63 | P.p(r, v[r.nextInt(v.size())]) 64 | } 65 | 66 | boolean beats(Move p1, Move p2) { 67 | def map = [(Move.PAPER): Move.ROCK, (Move.SCISSORS): Move.PAPER, (Move.ROCK): Move.SCISSORS] 68 | map[p1] == p2 69 | } 70 | 71 | boolean draw(Move m1, Move m2) { 72 | m1 == m2 73 | } 74 | 75 | MoveResult result(Move m1, Move m2) { 76 | draw(m1, m2) ? MoveResult.DRAW : (beats(m1, m2) ? MoveResult.WIN : MoveResult.LOSE) 77 | } 78 | 79 | } 80 | 81 | 82 | -------------------------------------------------------------------------------- /demo/src/main/groovy/com/github/mperry/fg/SimpleIODemoFunctional.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.Unit 5 | import fj.data.Option 6 | import fj.data.Stream 7 | import groovy.transform.TypeChecked 8 | 9 | import static com.github.mperry.fg.IOConstants.stdinReadLine 10 | import static com.github.mperry.fg.IOConstants.stdoutWriteLine 11 | import static fj.data.Option.none 12 | import static fj.data.Option.some 13 | 14 | @TypeChecked 15 | class SimpleIODemoFunctional { 16 | 17 | final String quit = "q" 18 | final String help = "The Spectacular Squaring REPL!\nEnter an integer to square or enter $quit to quit" 19 | final String prompt = ">" 20 | 21 | Option toInt(String s) { 22 | s.isInteger() ? some(s.toInteger()) : none() 23 | } 24 | 25 | String squareMessage(Integer n) { 26 | "square $n = ${n * n}" 27 | } 28 | 29 | Option> squareOptionIO(String s) { 30 | toInt(s).map { Integer n -> 31 | stdoutWriteLine(squareMessage(n)) 32 | } 33 | } 34 | 35 | SimpleIO squareIO(String s) { 36 | squareOptionIO(s).orSome(IOConstants.empty()) 37 | } 38 | 39 | Boolean isLoop(String s) { 40 | !isQuit(s) 41 | } 42 | 43 | Boolean isQuit(String s) { 44 | s == quit 45 | } 46 | 47 | Boolean validMessage(String s) { 48 | (s.isInteger() || isQuit(s)) 49 | } 50 | 51 | Option invalidMessage(String s) { 52 | validMessage(s) ? none() : Option. some("Not an integer: $s") 53 | } 54 | 55 | Option> invalidMessageOptionIO(String s) { 56 | invalidMessage(s).map { String it -> stdoutWriteLine(it) } 57 | } 58 | 59 | SimpleIO invalidMessageIO(String s) { 60 | invalidMessageOptionIO(s).orSome(IOConstants.empty()) 61 | } 62 | 63 | SimpleIO interaction() { 64 | stdoutWriteLine(prompt).append(stdinReadLine()).flatMap1({ String s -> 65 | invalidMessageIO(s).append(squareIO(s)) 66 | } as F) 67 | } 68 | 69 | SimpleIO> interactionStream() { 70 | SimpleIO.sequenceWhile(Stream.repeat(interaction()), { String s -> isLoop(s) } as F) 71 | } 72 | 73 | SimpleIO> interactionStreamAsync() { 74 | SimpleIO.sequenceWhile(Stream.repeat(interaction()), { String s -> isLoop(s) } as F) 75 | } 76 | 77 | SimpleIO> repl() { 78 | stdoutWriteLine(help).append(interactionStream()) 79 | } 80 | 81 | static void main(def args) { 82 | def d = new SimpleIODemoFunctional() 83 | d.repl().run() 84 | } 85 | 86 | } 87 | -------------------------------------------------------------------------------- /demo/src/main/groovy/com/github/mperry/fg/SimpleIODemoImperative.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | @TypeChecked 6 | class SimpleIODemoImperative { 7 | 8 | String quit = "q" 9 | String help = "Squaring REPL\nEnter $quit to quit" 10 | String prompt = ">" 11 | 12 | static void main(def args) { 13 | def d = new SimpleIODemoImperative() 14 | d.repl() 15 | } 16 | 17 | void repl() { 18 | println(help) 19 | System.in.withReader { java.io.Reader r -> 20 | def doLoop = true 21 | while (doLoop) { 22 | println(prompt) 23 | def s = r.readLine() 24 | process(s) 25 | doLoop = continueLoop(s) 26 | } 27 | } 28 | } 29 | 30 | String squareMessage(Integer n) { 31 | "square $n = ${n * n}" 32 | } 33 | 34 | String invalidMessage(String s) { 35 | "Not an integer: $s" 36 | } 37 | 38 | Boolean continueLoop(String s) { 39 | s != quit 40 | } 41 | 42 | Boolean isQuit(String s) { 43 | !continueLoop(s) 44 | } 45 | 46 | Boolean validInput(String s) { 47 | s.isInteger() || isQuit(s) 48 | } 49 | 50 | void process(String s) { 51 | if (!validInput(s)) { 52 | println(invalidMessage(s)) 53 | } 54 | if (s.isInteger()) { 55 | println(squareMessage(s.toInteger())) 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /demo/src/main/groovy/com/github/mperry/fg/Test1.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: MarkPerry 8 | * Date: 13/12/13 9 | * Time: 9:10 PM 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | @TypeChecked 13 | class Test1 { 14 | 15 | void fjAsync() { 16 | def console = IOConstants.stdinReadLine() 17 | def io = console.fjPromise() 18 | println "Enter FunctionalJava promise value" 19 | def p = io.run() 20 | def v = p.claim() 21 | println "Future value was $v" 22 | } 23 | 24 | void gparsAsync() { 25 | def console = IOConstants.stdinReadLine() 26 | def io = console.gparsPromise() 27 | println "Enter GPars promise value" 28 | def p = io.run() 29 | def v = p.get() 30 | println "Future value was $v" 31 | } 32 | 33 | void javaAsync() { 34 | def s = SimpleIO.defaultService() 35 | def console = IOConstants.stdinReadLine() 36 | def io = console.future(s) 37 | println "Enter Java future value" 38 | def f = io.run() 39 | def v = f.get() 40 | println "Future value was $v" 41 | s.shutdown() 42 | } 43 | 44 | static void main(def args) { 45 | def t = new Test1() 46 | t.fjAsync() 47 | t.gparsAsync() 48 | t.javaAsync() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /demo/src/main/groovy/com/github/mperry/fg/di/App.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.di 2 | 3 | import com.github.mperry.fg.ReaderM 4 | import fj.data.Option 5 | import groovy.transform.TypeChecked 6 | import groovy.transform.TypeCheckingMode 7 | 8 | /** 9 | * Created by MarkPerry on 13/01/14. 10 | */ 11 | @TypeChecked 12 | class App { 13 | 14 | Config config 15 | 16 | @TypeChecked(TypeCheckingMode.SKIP) 17 | Option userEmail(Integer id) { 18 | run(ConfigReader.userEmail(id)) 19 | } 20 | 21 | @TypeChecked(TypeCheckingMode.SKIP) 22 | Option user(Integer id) { 23 | run(ConfigReader.user(id)) 24 | } 25 | 26 | String mailService() { 27 | run(ConfigReader.mailService()) 28 | } 29 | 30 | def A run(ReaderM reader) { 31 | reader.f(config) 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /demo/src/main/groovy/com/github/mperry/fg/di/Config.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.di 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 13/01/14. 7 | */ 8 | @TypeChecked 9 | class Config { 10 | 11 | UserRepository userRepository 12 | String mailServer 13 | 14 | // mail service, etc. 15 | 16 | } 17 | -------------------------------------------------------------------------------- /demo/src/main/groovy/com/github/mperry/fg/di/ConfigReader.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.di 2 | 3 | import com.github.mperry.fg.ReaderM 4 | import fj.data.Option 5 | import groovy.transform.TypeChecked 6 | 7 | import static com.github.mperry.fg.ReaderM.lift 8 | 9 | /** 10 | * Created by MarkPerry on 13/01/14. 11 | */ 12 | @TypeChecked 13 | class ConfigReader { 14 | 15 | static ReaderM> user(Integer id) { 16 | ReaderM.>lift { Config c -> c.userRepository.get(id) } 17 | } 18 | 19 | static ReaderM> user(String username) { 20 | ReaderM.>lift { Config c -> c.userRepository.find(username) } 21 | } 22 | 23 | static ReaderM> userEmail(Integer id) { 24 | user(id).map { Option o -> o.map { User u -> u.email } } 25 | } 26 | 27 | static ReaderM mailService() { 28 | ReaderM.lift { Config c -> c.mailServer } 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /demo/src/main/groovy/com/github/mperry/fg/di/User.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.di 2 | 3 | import groovy.transform.Canonical 4 | import groovy.transform.TypeChecked 5 | 6 | /** 7 | * Created by MarkPerry on 13/01/14. 8 | */ 9 | @TypeChecked 10 | @Canonical 11 | class User { 12 | 13 | Integer id 14 | String username 15 | String email 16 | 17 | } 18 | -------------------------------------------------------------------------------- /demo/src/main/groovy/com/github/mperry/fg/di/UserRepository.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.di 2 | 3 | import fj.data.Option 4 | import groovy.transform.TypeChecked 5 | 6 | /** 7 | * Created by MarkPerry on 13/01/14. 8 | */ 9 | @TypeChecked 10 | class UserRepository { 11 | 12 | Map map = [1: new User(1, "ian", "ian@dummy.com"), 3: new User(3, "mark", "mark@dummy.com")] 13 | 14 | Option get(Integer id) { 15 | Option.fromNull(map.get(id)) 16 | } 17 | 18 | Option find(String username) { 19 | Option.none() 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /demo/src/main/groovy/com/github/mperry/fg/state/Input.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.state 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 17/02/14. 7 | */ 8 | @TypeChecked 9 | enum Input { 10 | COIN, TURN 11 | } 12 | -------------------------------------------------------------------------------- /demo/src/main/groovy/com/github/mperry/fg/state/Machine.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.state 2 | 3 | import groovy.transform.Canonical 4 | import groovy.transform.TypeChecked 5 | 6 | /** 7 | * Created by MarkPerry on 17/02/14. 8 | */ 9 | @TypeChecked 10 | @Canonical 11 | class Machine { 12 | 13 | Boolean locked 14 | Integer items 15 | Integer coins 16 | 17 | } 18 | -------------------------------------------------------------------------------- /demo/src/main/groovy/com/github/mperry/fg/state/MachineSimulation.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.state 2 | 3 | import com.github.mperry.fg.State 4 | import groovy.transform.Canonical 5 | import groovy.transform.TypeChecked 6 | import groovy.transform.TypeCheckingMode 7 | 8 | import static com.github.mperry.fg.state.Input.COIN 9 | import static com.github.mperry.fg.state.Input.TURN 10 | 11 | /** 12 | * Created by MarkPerry on 18/02/14. 13 | */ 14 | @TypeChecked 15 | @Canonical 16 | class MachineSimulation { 17 | 18 | @TypeChecked(TypeCheckingMode.SKIP) 19 | State simulate(List list) { 20 | def s2 = State.get() 21 | simulate(list, s2) 22 | } 23 | 24 | State simulate(List list, State state) { 25 | if (list.empty) { 26 | state 27 | } else { 28 | def h = list.head() 29 | def s4 = state.map { Machine m -> 30 | next(m, h) 31 | } 32 | simulate(list.tail(), s4) 33 | } 34 | } 35 | 36 | Machine next(Machine m, Input i) { 37 | if (m.items == 0) { 38 | m 39 | } else if (i == COIN && !m.locked) { 40 | m 41 | } else if (i == TURN && m.locked) { 42 | m 43 | } else if (i == COIN && m.locked) { 44 | new Machine(false, m.items, m.coins + 1) 45 | } else if (i == TURN && !m.locked) { 46 | new Machine(true, m.items - 1, m.coins) 47 | } 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /demo/src/main/groovy/com/github/mperry/fg/trampoline/TrampolineClosureTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.trampoline 2 | 3 | import groovy.transform.TypeChecked 4 | import groovy.transform.TypeCheckingMode 5 | import org.junit.Test 6 | import org.junit.Assert 7 | import fj.F2 8 | 9 | /** 10 | * Created by MarkPerry on 21/01/14. 11 | */ 12 | @TypeChecked 13 | class TrampolineClosureTest { 14 | 15 | @Test 16 | @TypeChecked(TypeCheckingMode.SKIP) 17 | void test1() { 18 | def fact 19 | fact = { acc, it -> 20 | it <= 1 ? acc : fact.trampoline(it * acc, it - 1) 21 | }.trampoline() 22 | def v = fact.call(1, 3) 23 | println v 24 | Assert.assertTrue(v == 6) 25 | 26 | } 27 | 28 | @Test 29 | void test2() { 30 | def list = [1, 2, 3] 31 | def v = foldRight(list, 1, { Integer acc, Integer i -> acc * i } as F2) 32 | println v 33 | Assert.assertTrue(v == 6) 34 | } 35 | 36 | @TypeChecked(TypeCheckingMode.SKIP) 37 | // def foldRight(List list, B b, F2 f) { 38 | def B foldRight(List list, B b, F2 f) { 39 | 40 | def rec 41 | rec = { List l, B myB, F2 myF -> 42 | def val = rec.call(list.tail(), myB, myF) 43 | f.f(val, list.head) 44 | }.trampoline() 45 | def v = rec.call(list, b, f) 46 | println v 47 | v 48 | 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /demo/src/test/groovy/com/github/mperry/fg/KenoTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.P 5 | import fj.P1 6 | import fj.P2 7 | import fj.data.Stream 8 | import groovy.transform.TypeChecked 9 | import groovy.transform.TypeCheckingMode 10 | import org.junit.Test 11 | 12 | /** 13 | * Created by MarkPerry on 13/01/14. 14 | */ 15 | @TypeChecked 16 | class KenoTest { 17 | 18 | def random = new Random(0) 19 | 20 | @Test 21 | @TypeChecked(TypeCheckingMode.SKIP) 22 | void test5() { 23 | def max = 10 24 | def choose = 5 25 | def s2 = Stream.repeat(random) 26 | def s3 = s2.map { Random r -> r.nextInt(max) + 1 } 27 | 28 | println s3.take(max).toList() 29 | def s4 = removeDuplicates(s3, max) 30 | println s4.take(max).toList() 31 | println s4.take(choose).toList() 32 | } 33 | 34 | @TypeChecked(TypeCheckingMode.SKIP) 35 | Stream removeDuplicates(Stream s, Integer max) { 36 | if (s.empty || max == 0) { 37 | s 38 | } else { 39 | def h = s.head() 40 | Stream.cons(h, { -> 41 | def s2 = (max == 1) ? (Stream) Stream.nil() : s.tail()._1() 42 | def rest = s2.filter { Integer i -> i != h } 43 | removeDuplicates(rest, max - 1) 44 | } as P1) 45 | } 46 | 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /demo/src/test/groovy/com/github/mperry/fg/TrampolineTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F2 5 | import groovy.transform.TypeChecked 6 | import org.junit.Test 7 | 8 | /** 9 | * Created by MarkPerry on 13/01/14. 10 | */ 11 | @TypeChecked 12 | class TrampolineTest { 13 | 14 | @Test 15 | void test1() { 16 | def list = fj.data.List.list(1, 4, 6, 7) 17 | 18 | def val = list.foldRightC({ Integer a, Integer acc -> a + acc } as F2, 0) 19 | println val.run() 20 | 21 | } 22 | 23 | 24 | } 25 | -------------------------------------------------------------------------------- /demo/src/test/groovy/com/github/mperry/fg/state/MachineTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.state 2 | 3 | import groovy.transform.TypeChecked 4 | import org.junit.Assert 5 | import org.junit.Test 6 | 7 | import static com.github.mperry.fg.state.Input.COIN 8 | import static com.github.mperry.fg.state.Input.TURN 9 | import static org.junit.Assert.assertTrue 10 | 11 | /** 12 | * Created by MarkPerry on 18/02/14. 13 | */ 14 | @TypeChecked 15 | class MachineTest { 16 | 17 | @Test 18 | void test1() { 19 | def m = new Machine(true, 5, 0) 20 | def sim = new MachineSimulation() 21 | def inputs = [COIN, TURN, TURN, COIN, COIN, TURN] 22 | def s = sim.simulate(inputs) 23 | def m2 = (Machine) s.eval(m) 24 | // println m2 25 | assertTrue(m2 == new Machine(true, 3, 2)) 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | 2 | sonatypeUsername = incorrectUser 3 | sonatypePassword = incorrectPwd 4 | 5 | signingEnabled = false 6 | signing.keyId = incorrectKeyId 7 | signing.password = incorrectPwd 8 | signing.secretKeyRingFile = incorrectSecret 9 | 10 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mperry/functionalgroovy/fb1e13ff354724dc8bd39184f7f2f335dfcd941b/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jul 29 21:05:52 EST 2014 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=http\://services.gradle.org/distributions/gradle-2.6-all.zip 7 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /java8/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | apply plugin: 'groovy' 3 | 4 | uploadArchives.enabled = true 5 | 6 | sourceCompatibility = "1.8" 7 | targetCompatibility = "1.8" 8 | 9 | dependencies { 10 | compile gradleGroovy 11 | compile project(":core") 12 | compile "org.functionaljava:functionaljava-java8:$fjVersion" 13 | } 14 | -------------------------------------------------------------------------------- /java8/src/main/groovy/com/github/mperry/fg/Function1Extension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by mperry on 10/06/2014. 7 | */ 8 | @TypeChecked 9 | class Function1Extension { 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /java8/src/main/groovy/com/github/mperry/fg/Function2Extension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by mperry on 10/06/2014. 7 | */ 8 | @TypeChecked 9 | class Function2Extension { 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /java8/src/main/groovy/com/github/mperry/fg/OptionalExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | import java.util.function.BiFunction 6 | import java.util.function.Function 7 | import fj.data.* 8 | 9 | /** 10 | * Created by mperry on 10/06/2014. 11 | */ 12 | @TypeChecked 13 | class OptionalExtension { 14 | 15 | /** 16 | * Performs function application within an optional value (applicative functor pattern). 17 | */ 18 | static Optional apply(Optional o1, Optional> o2) { 19 | Function, Optional > f = { Function f2 -> o1.map(f2) } as Function 20 | o2.flatMap(f) 21 | } 22 | 23 | /** 24 | * Returns true is this optional value has a value and the given predicate function 25 | * holds on that value, false otherwise. 26 | */ 27 | static Boolean exists(Optional o1, Function f) { 28 | o1.isPresent() && f.apply(o1.get()) 29 | } 30 | 31 | static Boolean forall(Optional o1, Function f) { 32 | !o1.isPresent() || f.apply(o1.get()) 33 | } 34 | 35 | static void foreach(Optional o1, Function f) { 36 | if (o1.isPresent()) { 37 | f.apply(o1.get()) 38 | } 39 | } 40 | 41 | static Optional liftM2(Optional o1, Optional o2, BiFunction f) { 42 | o1.flatMap { A a -> 43 | o2.map { B b -> 44 | f.apply(a, b) 45 | } 46 | } 47 | } 48 | 49 | static B orElseGet(Optional o1, B b, Function f) { 50 | o1.isPresent() ? f.apply(o1.get()) : b 51 | } 52 | 53 | /** 54 | * Returns the value from this optional value, or if there is no value, returns null. 55 | * This is intended for interfacing with APIs that expect a null for non-existence. 56 | * 57 | * @return This optional value or null if there is no value. 58 | */ 59 | static A toNull(Optional o1) { 60 | o1.orElse((A) null) 61 | } 62 | 63 | static Optional orElse(Optional o1, Optional o2) { 64 | o1.isPresent() ? o1 : o2 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /java8/src/main/groovy/com/github/mperry/fg/OptionalStaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F3 5 | import fj.data.Java8 6 | import fj.data.Option 7 | import groovy.transform.TypeChecked 8 | 9 | import java.util.function.BiFunction 10 | import java.util.function.Function 11 | 12 | import static fj.data.List.cons_ 13 | 14 | /** 15 | * Created by mperry on 10/06/2014. 16 | */ 17 | @TypeChecked 18 | class OptionalStaticExtension { 19 | 20 | static Optional join(Optional z, Optional> o) { 21 | def id = Function.>identity() 22 | o.flatMap(id) 23 | } 24 | 25 | static Function>, Optional> join(Optional z) { 26 | { Optional> o1 -> 27 | join(z, o1) 28 | } as Function 29 | } 30 | 31 | static F3, Optional, BiFunction, Optional> liftM2(Optional z) { 32 | { Optional o1, Optional o2, BiFunction f -> 33 | OptionalExtension.liftM2(o1, o2, f) 34 | } as F3 35 | } 36 | 37 | 38 | static Optional> sequence(Optional z, List> list) { 39 | Function, List> cons = { List list2 -> } 40 | list.isEmpty() ? Optional.of([]) : 41 | list.head().flatMap { A a -> 42 | sequence(z, list.tail()).map(Java8.F_Function(ListJavaExtension.cons(a))) 43 | 44 | } 45 | } 46 | 47 | static List ofs(Optional z, List> list) { 48 | (list.filter { Optional o -> o.isPresent() }) as List 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /java8/src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule: -------------------------------------------------------------------------------- 1 | moduleName=functionalgroovy-java8 2 | moduleVersion=@version@ 3 | extensionClasses=com.github.mperry.fg.Function1Extension,com.github.mperry.fg.Function2Extension,com.github.mperry.fg.OptionalExtension 4 | staticExtensionClasses=com.github.mperry.fg.OptionalStaticExtension 5 | -------------------------------------------------------------------------------- /kind/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | ext { 3 | 4 | } 5 | 6 | uploadArchives.enabled = true 7 | 8 | dependencies { 9 | compile gradleGroovy 10 | compile gradleFj 11 | compile project(":core") 12 | compile project(":typeclass") 13 | testCompile gradleJunit 14 | } 15 | 16 | 17 | -------------------------------------------------------------------------------- /kind/src/main/groovy/com/github/mperry/fg/FlatMap.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import groovy.transform.Canonical 5 | import groovy.transform.TypeChecked 6 | 7 | /** 8 | * Created by MarkPerry on 29/11/2014. 9 | */ 10 | @TypeChecked 11 | @Canonical 12 | class FlatMap extends Free { 13 | 14 | Free sub // subcomputation 15 | F> k // continue 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /kind/src/main/groovy/com/github/mperry/fg/Free.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F1Functions 5 | import fj.P1 6 | import groovy.transform.TypeChecked 7 | 8 | /** 9 | * Created by MarkPerry on 29/11/2014. 10 | */ 11 | @TypeChecked 12 | abstract class Free { 13 | 14 | def Free flatMap(F> f) { 15 | new FlatMap(this, f) 16 | } 17 | 18 | def Free map(F f) { 19 | flatMap(F1Functions.andThen(f, { B b -> new Return(b) } )) 20 | } 21 | 22 | Free step(Free f) { 23 | f 24 | } 25 | 26 | 27 | 28 | static A runTrampoline(Free, A> free) { 29 | 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /kind/src/main/groovy/com/github/mperry/fg/FreeP1.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.P1 4 | 5 | /** 6 | * Created by MarkPerry on 29/11/2014. 7 | */ 8 | class FreeP1 extends Free, A> { 9 | } 10 | -------------------------------------------------------------------------------- /kind/src/main/groovy/com/github/mperry/fg/FreeP1Monad.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import com.github.mperry.fg.typeclass.* 4 | import fj.F 5 | 6 | /** 7 | * Created by MarkPerry on 29/11/2014. 8 | */ 9 | class FreeP1Monad extends Monad { 10 | 11 | @Override 12 | def FreeP1 flatMap(FreeP1 ma, F> f) { 13 | ma.flatMap(f) 14 | } 15 | 16 | @Override 17 | def FreeP1 unit(B b) { 18 | new Return(b) 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /kind/src/main/groovy/com/github/mperry/fg/Return.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.Canonical 4 | import groovy.transform.TypeChecked 5 | 6 | /** 7 | * Created by MarkPerry on 29/11/2014. 8 | */ 9 | @TypeChecked 10 | @Canonical 11 | class Return extends Free { 12 | 13 | A a 14 | 15 | static Return unit(A a) { 16 | new Return(a) 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /kind/src/main/groovy/com/github/mperry/fg/Suspend.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.Canonical 4 | import groovy.transform.TypeChecked 5 | 6 | /** 7 | * Created by MarkPerry on 29/11/2014. 8 | */ 9 | @TypeChecked 10 | @Canonical 11 | class Suspend extends Free { 12 | 13 | K value 14 | 15 | static Suspend unit(K v) { 16 | new Suspend(v) 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /main/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | ext { 3 | 4 | } 5 | 6 | dependencies { 7 | compile project(":core") 8 | compile project(":typeclass") 9 | compile "org.gcontracts:gcontracts-core:1.2.12" 10 | compile "org.codehaus.gpars:gpars:1.2.1" 11 | compile("com.netflix.rxjava:rxjava-core:0.19.6") 12 | 13 | testCompile gradleJunit 14 | // testCompile "org.gcontracts:gcontracts-core:1+" 15 | testRuntime 'com.h2database:h2:1.4.181' 16 | } 17 | 18 | uploadArchives.enabled = true 19 | 20 | processResources { 21 | doFirst { 22 | filter(org.apache.tools.ant.filters.ReplaceTokens, tokens: [version: project.version]) 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/CollectionExtension2.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 12/04/2014. 7 | */ 8 | @TypeChecked 9 | class CollectionExtension2 { 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/CollectionStaticExtension2.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 12/04/2014. 7 | */ 8 | @TypeChecked 9 | class CollectionStaticExtension2 { 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/Comprehension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | import groovy.transform.TypeCheckingMode 5 | 6 | /** 7 | * Created with IntelliJ IDEA. 8 | * User: MarkPerry 9 | * Date: 26/02/13 10 | * Time: 12:36 AM 11 | * To change this template use File | Settings | File Templates. 12 | */ 13 | @TypeChecked 14 | class Comprehension { 15 | 16 | private static final String GUARD = "guard" 17 | private List generators = [] 18 | 19 | @TypeChecked 20 | def yield(Closure c) { 21 | startProcessing(c) 22 | } 23 | 24 | private def startProcessing(Closure yieldClosure) { 25 | def head = generators.head() 26 | process(yieldClosure, generators.tail(), [:], executeGenerator(head.func, [:]), head.name) 27 | } 28 | 29 | @TypeChecked 30 | private Object executeFunction(Closure c, Object context) { 31 | c.setDelegate(context) 32 | c.resolveStrategy = Closure.DELEGATE_ONLY 33 | c.call() 34 | } 35 | 36 | private Object executeYield(Closure c, Yield context) { 37 | executeFunction(c, context) 38 | } 39 | 40 | // @TypeChecked 41 | private def executeGenerator(Closure c, Object context) { 42 | executeFunction(c, context) 43 | } 44 | 45 | /** 46 | * Process stream, when doing an action over stream, do so with 47 | * lastVar in the context 48 | * strucutre must have map, filter and bind implemented 49 | */ 50 | @TypeChecked(TypeCheckingMode.SKIP) 51 | private def process(Closure yield, List gs, Map context, def structure, String lastVar) { 52 | if (gs.size() == 0) { 53 | structure.map { 54 | executeYield(yield, new Yield(values: context + [(lastVar): it])) 55 | } 56 | } else { 57 | def head = gs.head() 58 | def tail = gs.tail() 59 | if (head.guard) { 60 | def s = structure.filter { 61 | def z = executeFunction(head.func, context + [(lastVar): it]) 62 | z 63 | } 64 | process(yield, tail, context, s, lastVar) 65 | } else { 66 | structure.flatMap { 67 | def c = context + [(lastVar): it] 68 | process(yield, tail, c, executeGenerator(head.func, c), head.name) 69 | } 70 | } 71 | } 72 | } 73 | 74 | @TypeChecked 75 | Boolean isGuard(String name) { 76 | name == GUARD 77 | } 78 | 79 | @TypeChecked(TypeCheckingMode.SKIP) 80 | private void methodMissing(String name, args) { 81 | addGenerator(new Generator(name: name, func: args[0], guard: isGuard(name))) 82 | } 83 | 84 | Generator propertyMissing(String name) { 85 | def g = new Generator(name: name, guard: false) 86 | addGenerator(g) 87 | g 88 | } 89 | 90 | void addGenerator(Generator g) { 91 | generators << g 92 | } 93 | 94 | // @TypeChecked 95 | static def foreach(Closure comprehension) { 96 | comprehension.delegate = new Comprehension() 97 | comprehension.resolveStrategy = Closure.OWNER_FIRST 98 | comprehension.call() 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/FutureExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.P1 4 | import fj.control.parallel.Promise 5 | import fj.control.parallel.Strategy 6 | import groovy.transform.TypeChecked 7 | 8 | import java.util.concurrent.Future 9 | 10 | import static fj.control.parallel.Promise.promise 11 | import static fj.control.parallel.Strategy.simpleThreadStrategy 12 | 13 | /** 14 | * Created by MarkPerry on 18/04/2014. 15 | */ 16 | @TypeChecked 17 | class FutureExtension { 18 | 19 | static P1 p(Future f ) { 20 | Strategy.obtain(f) 21 | } 22 | 23 | static Promise promise(Future f) { 24 | FutureExtension.promise(f, simpleThreadStrategy()) 25 | } 26 | 27 | static Promise promise(Future f, Strategy s) { 28 | Promise.promise(s, p(f)) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/FutureMonad.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 18/04/2014. 7 | */ 8 | @TypeChecked 9 | class FutureMonad { 10 | 11 | 12 | 13 | } 14 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/Generator.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.data.Option 4 | import fj.data.Stream 5 | import groovy.transform.Canonical 6 | import groovy.transform.TypeChecked 7 | 8 | /** 9 | * Created with IntelliJ IDEA. 10 | * User: MarkPerry 11 | * Date: 25/02/13 12 | * Time: 11:48 PM 13 | * To change this template use File | Settings | File Templates. 14 | */ 15 | @Canonical 16 | @TypeChecked 17 | class Generator { 18 | 19 | String name 20 | Closure func 21 | Boolean guard = false 22 | 23 | static Boolean ALLOW_UKNOWN_TYPES = true 24 | 25 | // left shift should only accept a Functor or Monad 26 | 27 | def leftShift(final Stream s) { 28 | func = { s } 29 | } 30 | 31 | def leftShift(final List list) { 32 | func = { list } 33 | } 34 | 35 | def leftShift(final Collection c) { 36 | func = { c } 37 | } 38 | 39 | def leftShift(final Option o) { 40 | func = { o } 41 | } 42 | 43 | def leftShift(final Closure c) { 44 | func = c 45 | } 46 | 47 | def leftShift(final Object o) { 48 | if (ALLOW_UKNOWN_TYPES) { 49 | func = { o } 50 | } else { 51 | throw new UnsupportedOperationException("Comprehension not supported for object $o with class ${o.class.canonicalName}") 52 | } 53 | 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/IOConstants.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg; 2 | 3 | import fj.Unit 4 | import fj.data.Option 5 | import groovy.transform.TypeChecked; 6 | 7 | /** 8 | * Created with IntelliJ IDEA. 9 | * User: MarkPerry 10 | * Date: 8/11/13 11 | * Time: 10:21 AM 12 | * To change this template use File | Settings | File Templates. 13 | */ 14 | @TypeChecked 15 | class IOConstants { 16 | 17 | static Option console() { 18 | Option.fromNull(System.console()) 19 | } 20 | 21 | static SimpleIO stdinReadLine() { 22 | new SimpleIO() { 23 | String run() { 24 | System.in.newReader().readLine() 25 | } 26 | } 27 | } 28 | 29 | static SimpleIO> consoleReadLineOption() { 30 | new SimpleIO>() { 31 | Option run() { 32 | Option.fromNull(System.in.withReader { java.io.Reader it -> 33 | it.readLine() 34 | }) 35 | } 36 | } 37 | } 38 | 39 | static SimpleIO stdoutWriteLine(final String msg) { 40 | new SimpleIO() { 41 | Unit run() { 42 | println(msg) 43 | Unit.unit() 44 | } 45 | } 46 | } 47 | 48 | static SimpleIO empty() { 49 | new SimpleIO() { 50 | Unit run() { 51 | Unit.unit() 52 | } 53 | } 54 | } 55 | 56 | 57 | } 58 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/Identity.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.Canonical 4 | import groovy.transform.TypeChecked 5 | 6 | /** 7 | * Created by mperry on 1/07/2014. 8 | */ 9 | @TypeChecked 10 | @Canonical 11 | class Identity { 12 | 13 | A item 14 | } 15 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/IdentityM.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import com.github.mperry.fg.typeclass.Monad 4 | import fj.F 5 | import fj.Unit 6 | import groovy.transform.Canonical 7 | import groovy.transform.TypeChecked 8 | 9 | /** 10 | * Created by MarkPerry on 9/01/14. 11 | */ 12 | @TypeChecked 13 | @Canonical 14 | class IdentityM extends Monad { 15 | 16 | A item 17 | 18 | private IdentityM(A a) { 19 | item = a 20 | } 21 | 22 | static IdentityM idUnit() { 23 | lift(Unit.unit()) 24 | } 25 | 26 | static IdentityM lift(B b) { 27 | new IdentityM(b) 28 | } 29 | 30 | @Override 31 | def IdentityM flatMap(IdentityM mb, F> f) { 32 | f.f(mb.item) 33 | } 34 | 35 | @Override 36 | def IdentityM unit(B b) { 37 | lift(b) 38 | } 39 | 40 | def IdentityM flatMap(F> f) { 41 | flatMap(this, f) 42 | } 43 | 44 | 45 | } 46 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/IdentityMonad.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import com.github.mperry.fg.typeclass.Monad 4 | import fj.F 5 | import groovy.transform.TypeChecked 6 | 7 | /** 8 | * Created by mperry on 1/07/2014. 9 | */ 10 | @TypeChecked 11 | class IdentityMonad extends Monad { 12 | 13 | @Override 14 | def Identity flatMap(Identity id, F> f) { 15 | f.f(id.item) 16 | } 17 | 18 | @Override 19 | def Identity unit(B b) { 20 | new Identity(b) 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/ListMonadExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import com.github.mperry.fg.typeclass.concrete.ListApplicative 4 | import com.github.mperry.fg.typeclass.concrete.ListFunctor 5 | import com.github.mperry.fg.typeclass.concrete.ListMonad 6 | import fj.F 7 | import fj.F2 8 | import fj.F3 9 | import fj.Unit 10 | import groovy.transform.TypeChecked 11 | 12 | //import groovy.transform.TypeChecked 13 | //import groovy.transform.TypeCheckingMode 14 | 15 | /** 16 | * Created by MarkPerry on 16/01/14. 17 | */ 18 | @TypeChecked 19 | class ListMonadExtension { 20 | 21 | static ListMonad monad() { 22 | new ListMonad() 23 | } 24 | 25 | static List ap(List ma, List> mf) { 26 | monad().ap(ma, mf) 27 | } 28 | 29 | static List to(List list, B b) { 30 | monad().to(list, b) 31 | } 32 | 33 | static List skip(List list) { 34 | monad().skip(list) 35 | } 36 | 37 | static List> traverse(List list, F> f) { 38 | monad().traverse(list, f) 39 | } 40 | 41 | static List> replicateM(List list, Integer n) { 42 | monad().replicateM(n, list) 43 | } 44 | 45 | static List liftM(List ma, F f) { 46 | monad().liftM(ma, f) 47 | } 48 | 49 | static List liftM2(List ma, List mb, F2 f) { 50 | monad().liftM2(ma, mb, f) 51 | } 52 | 53 | static List liftM3(List ma, List mb, List mc, F3 f) { 54 | monad().liftM3(ma, mb, mc, f) 55 | } 56 | 57 | 58 | static List map2(List listA, List listB, F2 f) { 59 | monad().map2(listA, listB, f) 60 | } 61 | 62 | // Applicative 63 | 64 | static ListApplicative applicative() { 65 | new ListApplicative() 66 | } 67 | 68 | static List apply(List list, List> listFs) { 69 | applicative().apply(listFs, list) 70 | } 71 | 72 | static List liftA(List a1, F f) { 73 | applicative().liftA(f, a1) 74 | } 75 | 76 | 77 | static List liftA2(List listAs, List listBs, F2 f) { 78 | applicative().liftA2(f, listAs, listBs) 79 | } 80 | 81 | static List liftA3(List apa, List apb, List apc, F3 f) { 82 | applicative().liftA3(f, apa, apb, apc) 83 | } 84 | 85 | } 86 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/ListMonadStaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import com.github.mperry.fg.typeclass.concrete.ListMonad 4 | import fj.F 5 | import fj.F2 6 | import fj.Unit 7 | import fj.data.Stream 8 | import groovy.transform.TypeChecked 9 | 10 | //import groovy.transform.TypeChecked 11 | //import groovy.transform.TypeCheckingMode 12 | 13 | /** 14 | * Created by MarkPerry on 16/01/14. 15 | */ 16 | @TypeChecked 17 | class ListMonadStaticExtension { 18 | 19 | static ListMonad monad() { 20 | new ListMonad() 21 | } 22 | 23 | static List join(List list, List> list2) { 24 | monad().join(list2) 25 | } 26 | 27 | 28 | static List> filterM(List z, List list, F> f) { 29 | monad().filterM(list, f) 30 | } 31 | 32 | static List foldM(List l, Stream s, B b, F2> f) { 33 | monad().foldM(s, b, f) 34 | } 35 | 36 | static List foldM_(List l, Stream s, B b, F2> f) { 37 | monad().foldM_(s, b, f) 38 | } 39 | 40 | static List foldM(List z, List s, B b, F2> f) { 41 | monad().foldM(s, b, f) 42 | } 43 | 44 | static List foldM_(List l, List s, B b, F2> f) { 45 | monad().foldM_(s, b, f) 46 | } 47 | 48 | static List> sequence(List l, List> list) { 49 | monad().sequence(list) 50 | } 51 | 52 | static F> compose(List l, F> f, F> g) { 53 | monad().compose(f, g) 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/RandStateInteger.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 17/02/14. 7 | */ 8 | @TypeChecked 9 | class RandStateInteger extends State { 10 | 11 | 12 | 13 | } 14 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/ReaderM.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F1Functions 5 | import groovy.transform.TypeChecked 6 | import groovy.transform.TypeCheckingMode 7 | 8 | /** 9 | * Created by MarkPerry on 11/01/14. 10 | */ 11 | @TypeChecked 12 | class ReaderM { 13 | 14 | F function 15 | 16 | ReaderM(F f) { 17 | function = f 18 | } 19 | 20 | B f(A a) { 21 | function.f(a) 22 | } 23 | 24 | static ReaderM lift(F f) { 25 | new ReaderM(f) 26 | } 27 | 28 | def ReaderM map(F f) { 29 | lift(F1Functions.andThen(function, f)) 30 | } 31 | 32 | def ReaderM andThen(F f) { 33 | map(f) 34 | } 35 | 36 | def ReaderM flatMap(F> f) { 37 | lift({A a -> f.f(function.f(a)).f(a)} as F) 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/SetExtension2.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import com.github.mperry.fg.typeclass.Monad 4 | import com.github.mperry.fg.typeclass.concrete.SetMonad 5 | import fj.F 6 | import fj.F2 7 | import fj.F3 8 | import fj.Unit 9 | import fj.data.Stream 10 | import groovy.transform.TypeChecked 11 | 12 | /** 13 | * Created by MarkPerry on 12/04/2014. 14 | */ 15 | @TypeChecked 16 | class SetExtension2 { 17 | 18 | static SetMonad monad() { 19 | new SetMonad() 20 | } 21 | 22 | static Set create() { 23 | new HashSet() 24 | } 25 | 26 | // static Set map(Set ma, F f) { 27 | // monad().map(f, ma) 28 | // } 29 | 30 | 31 | static Set apply(Set sa, Set> sf) { 32 | monad().apply(sf, sa) 33 | } 34 | 35 | static Set flatMap(Set ma, F> f){ 36 | monad().flatMap(ma, f) 37 | } 38 | 39 | static Set map(Set ma, F f) { 40 | def result = this.create() 41 | for (A a: ma) { 42 | result.add(f.f(a)) 43 | } 44 | result 45 | } 46 | 47 | static Set map2(Set ma, Set mb, F2 f) { 48 | monad().map2(ma, mb, f) 49 | } 50 | 51 | static Set to(Set ma, B b) { 52 | monad().to(ma, b) 53 | } 54 | 55 | static Set skip(Set ma) { 56 | monad().skip(ma) 57 | } 58 | 59 | static Set> replicateM(Set ma, Integer n) { 60 | monad().replicateM(n, ma) 61 | } 62 | 63 | static Set liftM(Set ma, F f) { 64 | monad().liftM(ma, f) 65 | } 66 | 67 | static Set liftM2(Set ma, Set mb, F2 f) { 68 | monad().liftM2(ma, mb, f) 69 | } 70 | 71 | static Set liftM3(Set ma, Set mb, Set mc, F3 f) { 72 | monad().liftM3(ma, mb, mc, f) 73 | } 74 | 75 | static Set ap(Set ma, Set> mf) { 76 | monad().ap(ma, mf) 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/SetMonadExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import com.github.mperry.fg.typeclass.concrete.ListApplicative 4 | import com.github.mperry.fg.typeclass.concrete.ListMonad 5 | import com.github.mperry.fg.typeclass.concrete.SetMonad 6 | import fj.F 7 | import fj.F2 8 | import fj.F3 9 | import fj.Unit 10 | import groovy.transform.TypeChecked 11 | 12 | /** 13 | * Created by MarkPerry on 13/04/2014. 14 | */ 15 | @TypeChecked 16 | class SetMonadExtension { 17 | 18 | static SetMonad monad() { 19 | new SetMonad() 20 | } 21 | 22 | static Set ap(Set ma, Set> mf) { 23 | monad().ap(ma, mf) 24 | } 25 | 26 | static Set to(Set list, B b) { 27 | monad().to(list, b) 28 | } 29 | 30 | static Set skip(Set list) { 31 | monad().skip(list) 32 | } 33 | 34 | static Set> replicateM(Set list, Integer n) { 35 | monad().replicateM(n, list) 36 | } 37 | 38 | static Set liftM(Set ma, F f) { 39 | monad().liftM(ma, f) 40 | } 41 | 42 | static Set liftM2(Set ma, Set mb, F2 f) { 43 | monad().liftM2(ma, mb, f) 44 | } 45 | 46 | static Set liftM3(Set ma, Set mb, Set mc, F3 f) { 47 | monad().liftM3(ma, mb, mc, f) 48 | } 49 | 50 | static Set map2(Set listA, Set listB, F2 f) { 51 | monad().map2(listA, listB, f) 52 | } 53 | 54 | // Applicative 55 | 56 | static Set apply(Set list, Set> listFs) { 57 | monad().apply(listFs, list) 58 | } 59 | 60 | static Set liftA(Set a1, F f) { 61 | monad().liftA(f, a1) 62 | } 63 | 64 | static Set liftA2(Set listAs, Set listBs, F2 f) { 65 | monad().liftA2(f, listAs, listBs) 66 | } 67 | 68 | static Set liftA3(Set apa, Set apb, Set apc, F3 f) { 69 | monad().liftA3(f, apa, apb, apc) 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/SetMonadStaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import com.github.mperry.fg.typeclass.concrete.ListMonad 4 | import com.github.mperry.fg.typeclass.concrete.SetMonad 5 | import fj.F 6 | import fj.F2 7 | import fj.Unit 8 | import fj.data.Stream 9 | import groovy.transform.TypeChecked 10 | 11 | /** 12 | * Created by MarkPerry on 13/04/2014. 13 | */ 14 | @TypeChecked 15 | class SetMonadStaticExtension { 16 | 17 | static SetMonad monad() { 18 | new SetMonad() 19 | } 20 | 21 | static Set join(Set list, Set> list2) { 22 | monad().join(list2) 23 | } 24 | 25 | static Set> filterM(Set z, List list, F> f) { 26 | monad().filterM(list, f) 27 | } 28 | 29 | static Set foldM(Set l, Stream s, B b, F2> f) { 30 | monad().foldM(s, b, f) 31 | } 32 | 33 | static Set foldM_(Set l, Stream s, B b, F2> f) { 34 | monad().foldM_(s, b, f) 35 | } 36 | 37 | static Set> sequence(Set l, List> list) { 38 | monad().sequence(list) 39 | } 40 | 41 | static F> compose(Set l, F> f, F> g) { 42 | monad().compose(f, g) 43 | } 44 | 45 | 46 | } 47 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/SetStaticExtension2.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import com.github.mperry.fg.typeclass.Monad 4 | import com.github.mperry.fg.typeclass.concrete.SetMonad 5 | import fj.F 6 | import fj.F2 7 | import fj.Unit 8 | import fj.data.Stream 9 | import groovy.transform.TypeChecked 10 | import groovy.transform.TypeCheckingMode 11 | 12 | /** 13 | * Created by MarkPerry on 12/04/2014. 14 | */ 15 | @TypeChecked 16 | class SetStaticExtension2 { 17 | 18 | static SetMonad monad() { 19 | new SetMonad() 20 | } 21 | 22 | static Set pure(Set z, A a) { 23 | monad().pure(a) 24 | } 25 | 26 | static Set unit(Set z, B b) { 27 | monad().unit(b) 28 | } 29 | 30 | 31 | static F> unit(Set z) { 32 | monad().unit() 33 | } 34 | 35 | static Set join(Set z, Set> mma) { 36 | monad().join(mma) 37 | } 38 | 39 | static Set foldM(Set z, List list, B b, F2> f) { 40 | monad().foldM(list, b, f) 41 | } 42 | 43 | static Set foldM_(Set z, Stream s, B b, F2> f) { 44 | monad().foldM_(s, b, f) 45 | } 46 | 47 | static Set foldM_(Set z, List s, B b, F2> f) { 48 | monad().foldM_(s, b, f) 49 | } 50 | 51 | static Set> sequence(Set z, List> list) { 52 | monad().sequence(list) 53 | } 54 | 55 | static Set> traverse(Set z, List list, F> f) { 56 | monad().traverse(list, f) 57 | } 58 | 59 | static F> compose(Set z, F> f, F> g) { 60 | monad().compose(f, g) 61 | } 62 | 63 | static Set> filterM(Set z, List list, F> f) { 64 | monad().filterM(list, f) 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/SimpleIOExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.P1 4 | import groovy.transform.TypeChecked 5 | import groovyx.gpars.GParsPoolUtil 6 | import groovyx.gpars.scheduler.FJPool 7 | 8 | import java.util.concurrent.Callable 9 | import java.util.concurrent.ExecutorService 10 | import java.util.concurrent.Future 11 | 12 | @TypeChecked 13 | class SimpleIOExtension { 14 | 15 | public static groovyx.gpars.dataflow.Promise asyncGpars(SimpleIO io) { 16 | def p = new FJPool() 17 | def c = { -> io.run() } 18 | def af = GParsPoolUtil.asyncFun(c, p) 19 | def r = af.call() 20 | (groovyx.gpars.dataflow.Promise) r 21 | } 22 | 23 | public static Future asyncJava(SimpleIO io) { 24 | asyncJava(io, SimpleIO.defaultService()) 25 | } 26 | 27 | public static Future asyncJava(SimpleIO io, ExecutorService s) { 28 | def c = { -> io.run() } 29 | def r = s.submit(c as Callable) 30 | r 31 | } 32 | 33 | public static fj.control.parallel.Promise asyncFj(SimpleIO io) { 34 | def c = { -> 35 | io.run() 36 | } 37 | def r = fj.control.parallel.Promise.promise(SimpleIO.defaultStrategy(), c as P1) 38 | r 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/SimpleIOStaticExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.control.Trampoline 5 | import fj.data.Stream 6 | import groovy.transform.TypeChecked 7 | 8 | /** 9 | * Created with IntelliJ IDEA. 10 | * User: MarkPerry 11 | * Date: 13/12/13 12 | * Time: 12:10 PM 13 | * To change this template use File | Settings | File Templates. 14 | */ 15 | @TypeChecked 16 | class SimpleIOStaticExtension { 17 | 18 | static SimpleIO> sequenceWhileR(SimpleIO clazz, Stream> stream, F f) { 19 | if (stream.empty) { 20 | SimpleIO.lift(Stream.nil()) 21 | } else { 22 | stream.head().flatMap({ A a -> 23 | if (!f.f(a)) { 24 | SimpleIO.lift(Stream.nil()) 25 | } else { 26 | def t = stream.tail()._1() 27 | sequenceWhileR(clazz, t, f).map({ Stream s -> s.cons(a)} as F, Stream>) 28 | } 29 | } as F) 30 | } 31 | } 32 | 33 | static Trampoline>> empty() { 34 | Trampoline.pure(SimpleIO.lift(Stream.nil())) 35 | } 36 | 37 | static Trampoline>> sequenceWhileC(SimpleIO clazz, Stream> stream, F f) { 38 | if (stream.empty) { 39 | empty() 40 | } else { 41 | // add loop here 42 | def io = stream.head().map({ A a -> 43 | def b = f.f(a) 44 | if (!b) { 45 | empty() 46 | } else { 47 | def tail = stream.tail()._1() 48 | sequenceWhileC(clazz, tail, f) 49 | } 50 | } as F>>>) 51 | SimpleIO.transform(io) 52 | } 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/SkiCalculus.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F2 5 | import fj.data.Either 6 | import groovy.transform.TypeChecked 7 | import groovy.transform.TypeCheckingMode 8 | 9 | import static fj.data.Either.left 10 | 11 | /** 12 | * Created by MarkPerry on 14/01/14. 13 | */ 14 | @TypeChecked 15 | class SkiCalculus { 16 | 17 | def X I(X x) { 18 | x 19 | } 20 | 21 | def F K(X x) { 22 | { Y y -> x } as F 23 | } 24 | 25 | def B S(F> x, F y, Z z) { 26 | (x.f(z)).f(y.f(z)) 27 | } 28 | 29 | /** 30 | * true 31 | * @param x 32 | * @param y 33 | * @return 34 | */ 35 | @TypeChecked(TypeCheckingMode.SKIP) 36 | def X True(X x, Y y) { 37 | K(x).f(y) 38 | } 39 | 40 | def F2 True_() { 41 | { X x, Y y -> True(x, y) } as F2 42 | } 43 | 44 | /** 45 | * false 46 | * @param x 47 | * @param y 48 | * @return 49 | */ 50 | @TypeChecked(TypeCheckingMode.SKIP) 51 | def Y False(X x, Y y) { 52 | K(y).f(K(x).f(y)) 53 | } 54 | 55 | def F2 False_() { 56 | { X x, Y y -> False(x, y) } as F2 57 | } 58 | 59 | def Either not(F2 f) { 60 | def val = f.f(False_(), True_()) 61 | (val instanceof A) ? left(val) : Either.right(val) 62 | } 63 | 64 | @TypeChecked(TypeCheckingMode.SKIP) 65 | def F2 or(F2 a, F2 b) { 66 | a.f(True_(), b) 67 | } 68 | 69 | @TypeChecked(TypeCheckingMode.SKIP) 70 | def F2 and(F2 a, F2 b) { 71 | a.f(b, False_()) 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/SqlExtension.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F1Functions 5 | import fj.Function 6 | import fj.data.Stream 7 | import groovy.sql.GroovyRowResult 8 | import groovy.sql.Sql 9 | import groovy.transform.TypeChecked 10 | import groovy.transform.TypeCheckingMode 11 | 12 | import java.sql.ResultSet 13 | import java.sql.SQLException 14 | 15 | /** 16 | * Created by MarkPerry on 2/02/14. 17 | */ 18 | @TypeChecked 19 | class SqlExtension { 20 | 21 | @TypeChecked(TypeCheckingMode.SKIP) 22 | static Stream streamRowResult(ResultSet rs, F f) { 23 | stream(rs).map { ResultSet it -> 24 | it.toRowResult() 25 | }.map(f) 26 | } 27 | 28 | @TypeChecked(TypeCheckingMode.SKIP) 29 | static Stream streamResultSet(ResultSet rs, F f) { 30 | stream(rs).map(f) 31 | } 32 | 33 | static rx.Observable observableRowResult(ResultSet rs, F f) { 34 | rx.Observable.from(streamRowResult(rs, f)) 35 | } 36 | 37 | static rx.Observable observableResultSet(ResultSet rs, F f) { 38 | rx.Observable.from(streamResultSet(rs, f)) 39 | } 40 | 41 | static rx.Observable observableRowResultI(ResultSet rs, F f) { 42 | rx.Observable.from(Stream.stream(1, 2, 3)) 43 | } 44 | 45 | static B fold(Sql sql, String query, F, B> g) { 46 | fold(sql, query, Function.identity(), g) 47 | } 48 | 49 | static B fold(Sql sql, String query, Closure f) { 50 | fold(sql, query, f as F) 51 | } 52 | 53 | static B fold(Sql sql, String query, F f, F, B> g) { 54 | def c 55 | def s 56 | try { 57 | c = sql.getConnection() 58 | s = c.createStatement() 59 | def rs = s.executeQuery(query) 60 | def b = g.f(streamResultSet(rs, f)) 61 | b 62 | } catch (SQLException e) { 63 | e.printStackTrace() 64 | } finally { 65 | // external function's responsibility to close the Sql resource 66 | // sql.close() 67 | } 68 | } 69 | 70 | @TypeChecked(TypeCheckingMode.SKIP) 71 | static F rowResult() { 72 | return { ResultSet rs -> rs.toRowResult() } as F 73 | } 74 | 75 | static B foldRowResult(Sql sql, String query, F, B> g) { 76 | foldRowResult(sql, query, Function.identity(), g) 77 | } 78 | 79 | static B foldRowResult(Sql sql, String query, Closure f) { 80 | foldRowResult(sql, query, f as F) 81 | } 82 | 83 | @TypeChecked(TypeCheckingMode.SKIP) 84 | static B foldRowResult(Sql sql, String query, F f, F, B> g) { 85 | fold(sql, query, F1Functions.o(f, rowResult()), g) 86 | } 87 | 88 | static Stream stream(ResultSet rs) { 89 | SqlExtensionJava.toStream(rs) 90 | } 91 | 92 | static Stream stream(Sql sql, String query) { 93 | try { 94 | def c = sql.getConnection() 95 | def s = c.createStatement() 96 | def rs = s.executeQuery(query) 97 | stream(rs) 98 | } catch (SQLException e) { 99 | throw e 100 | } 101 | } 102 | 103 | } 104 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/SqlExtensionJava.java: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg; 2 | 3 | import fj.F; 4 | import fj.P1; 5 | import fj.data.Stream; 6 | 7 | import java.sql.ResultSet; 8 | import java.sql.SQLException; 9 | 10 | /** 11 | * Created by MarkPerry on 2/02/14. 12 | */ 13 | public class SqlExtensionJava { 14 | 15 | static Stream toStream(final ResultSet rs, final F f) 16 | throws SQLException { 17 | return toStream(rs).map(f); 18 | } 19 | 20 | static Stream toStream(final ResultSet rs) throws SQLException { 21 | boolean b = rs.next(); 22 | if (!b) { 23 | return Stream.nil(); 24 | } else { 25 | return Stream.cons(rs, new P1>() { 26 | @Override 27 | public Stream _1() { 28 | try { 29 | return toStream(rs); 30 | } catch (SQLException e) { 31 | e.printStackTrace(); 32 | return Stream.nil(); 33 | } 34 | } 35 | }); 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/State.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F1Functions 5 | import fj.P 6 | import fj.P2 7 | import fj.Unit 8 | import groovy.transform.Canonical 9 | import groovy.transform.TypeChecked 10 | import groovy.transform.TypeCheckingMode 11 | 12 | /** 13 | * Created by MarkPerry on 9/01/14. 14 | */ 15 | @TypeChecked 16 | @Canonical 17 | class State { 18 | 19 | F> run 20 | 21 | P2 run(S s) { 22 | run.f(s) 23 | } 24 | 25 | static State lift(F> f) { 26 | new State(f) 27 | } 28 | 29 | static State liftS(F f) { 30 | State.lift({ S1 s -> 31 | def s2 = f.f(s) 32 | P.p(s2, s2) 33 | } as F) 34 | } 35 | 36 | static State unit(A1 a) { 37 | lift({ S1 s -> P.p(s, a)} as F) 38 | } 39 | 40 | def State map(F f) { 41 | State.lift({ S s -> 42 | def p2 = run(s) 43 | def b = f.f(p2._2()) 44 | P.p(p2._1(), b) 45 | } as F) 46 | } 47 | 48 | static State modify(F f) { 49 | State.get().flatMap { S s -> 50 | State.lift({ S s2 -> 51 | // def s3 = f.f(s) 52 | P.p(f.f(s), Unit.unit()) 53 | } as F) 54 | 55 | } 56 | } 57 | 58 | def State map(Closure c) { 59 | map(c as F) 60 | } 61 | 62 | def State mapState(F, P2> f) { 63 | new State({ S s -> 64 | def p = run(s) 65 | f.f(p) 66 | } as F) 67 | } 68 | 69 | static State flatMap(State mb, F> f) { 70 | mb.flatMap(f) 71 | } 72 | 73 | 74 | def State flatMap(F> f) { 75 | new State({ S s -> 76 | def p = run(s) 77 | def a = p._2() 78 | def s2 = p._1() 79 | def smb = f.f(a) 80 | smb.run(s2) 81 | } as F) 82 | } 83 | 84 | def State flatMap(Closure> c) { 85 | flatMap(c as F) 86 | } 87 | 88 | def State unit(F> f) { 89 | lift(f) 90 | } 91 | 92 | static State get() { 93 | def f = { S1 s -> P.p(s, s) } 94 | State.lift(f as F) 95 | } 96 | 97 | State gets() { 98 | State.lift({ S s -> 99 | def p = run(s) 100 | def s2 = p._1() 101 | P.p(s2, s2) 102 | } as F) 103 | } 104 | 105 | static State put(S1 s) { 106 | State.lift({ Object z -> P.p(s, Unit.unit())} as F) 107 | } 108 | 109 | A eval(S s) { 110 | run(s)._2() 111 | } 112 | 113 | S exec(S s) { 114 | run(s)._1() 115 | } 116 | 117 | State withs(F f) { 118 | lift(F1Functions.andThen(f, run)) 119 | } 120 | 121 | static State gets(F f) { 122 | State.get().map({ S s -> f.f(s)} as F) 123 | } 124 | 125 | } 126 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/StateInt.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.P2 5 | import groovy.transform.Canonical 6 | import groovy.transform.TypeChecked 7 | 8 | /** 9 | * Created by MarkPerry on 9/01/14. 10 | */ 11 | @TypeChecked 12 | @Canonical 13 | class StateInt extends State { 14 | 15 | StateInt(F> f) { 16 | run = f 17 | } 18 | 19 | def StateInt flatMap(F> f) { 20 | new StateInt({ Integer s -> 21 | def p = run.f(s) 22 | def a = p._2() 23 | def s2 = p._1() 24 | 25 | def sib = f.f(a) 26 | sib.run.f(s2) 27 | } as F) 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/StateIntMonad.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import com.github.mperry.fg.typeclass.Monad 4 | import fj.F 5 | import fj.P 6 | import groovy.transform.Canonical 7 | import groovy.transform.TypeChecked 8 | import groovy.transform.TypeCheckingMode 9 | 10 | /** 11 | * Created by MarkPerry on 9/01/14. 12 | */ 13 | @TypeChecked 14 | @Canonical 15 | class StateIntMonad extends Monad { 16 | 17 | @Override 18 | @TypeChecked(TypeCheckingMode.SKIP) 19 | def StateInt flatMap(StateInt mb, F> f) { 20 | mb.flatMap(f) 21 | } 22 | 23 | @Override 24 | @TypeChecked(TypeCheckingMode.SKIP) 25 | def StateInt unit(B b) { 26 | new StateInt({ Integer s -> P.p(b, s) } as F) 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/StreamExtension2.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 20/01/14. 7 | */ 8 | @TypeChecked 9 | class StreamExtension2 { 10 | 11 | 12 | } 13 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/StreamStaticExtension2.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.P2 5 | import fj.data.Option 6 | import fj.data.Stream 7 | import groovy.transform.TypeChecked 8 | import groovy.transform.TypeCheckingMode 9 | 10 | /** 11 | * Created by MarkPerry on 20/01/14. 12 | */ 13 | @TypeChecked 14 | class StreamStaticExtension2 { 15 | 16 | @TypeChecked(TypeCheckingMode.SKIP) 17 | static Stream unfold(Stream s, B b, F>> f) { 18 | Stream.unfold(f, b) 19 | } 20 | 21 | @TypeChecked(TypeCheckingMode.SKIP) 22 | static Stream unfold(Stream s, B b, Closure>> f) { 23 | unfold(s, b, f as F) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/TypeLambda.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import com.github.mperry.fg.typeclass.Monad 4 | import groovy.transform.TypeChecked 5 | 6 | /** 7 | * Created by MarkPerry on 9/01/14. 8 | */ 9 | @TypeChecked 10 | class TypeLambda { 11 | 12 | def GroovyClassLoader newLoader() { 13 | new GroovyClassLoader(this.class.classLoader) 14 | // new GroovyClassLoader() 15 | } 16 | 17 | def Class> partialStateApplication(GroovyClassLoader loader, Class stateClass) { 18 | def stateType = stateClass.simpleName 19 | def name = "State${stateType}Dynamic" 20 | 21 | def s = """ 22 | package com.github.mperry.fg 23 | 24 | import fj.F 25 | import fj.P2 26 | import groovy.transform.Canonical 27 | import com.github.mperry.fg.typeclass.Monad 28 | 29 | @Canonical 30 | class $name extends State<$stateType, A> { 31 | 32 | $name(F<$stateType, P2<$stateType, A>> f) { 33 | run = f 34 | } 35 | 36 | def $name flatMap(F> f) { 37 | new $name({ $stateType s -> 38 | def p = run.f(s) 39 | def a = p._2() 40 | def s2 = p._1() 41 | def sib = f.f(a) 42 | sib.run.f(s2) 43 | } as F) 44 | } 45 | 46 | } 47 | """ 48 | def clazz = loader.parseClass(s) 49 | // println("parital type application: $s") 50 | clazz 51 | } 52 | 53 | def Class stateMonad(GroovyClassLoader loader, Class> partialStateClass, Class stateClass) { 54 | def partialStateType = partialStateClass.simpleName 55 | def stateType = stateClass.simpleName 56 | def name = "${partialStateType}Monad" 57 | 58 | def s = """ 59 | package com.github.mperry.fg 60 | 61 | import fj.F 62 | import fj.P2 63 | import fj.P 64 | import groovy.transform.Canonical 65 | import com.github.mperry.fg.typeclass.Monad 66 | 67 | @Canonical 68 | class $name extends Monad<$partialStateType> { 69 | def $partialStateType flatMap($partialStateType mb, F> f) { 70 | mb.flatMap(f) 71 | } 72 | 73 | def $partialStateType unit(B b) { 74 | new $partialStateType({ $stateType s -> P.p(s, b) } as F) 75 | } 76 | 77 | } 78 | """ 79 | def clazz = loader.parseClass(s) 80 | // println "monad: $s" 81 | clazz 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/WriterM.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F2 5 | import groovy.transform.TypeChecked 6 | 7 | /** 8 | * Created by MarkPerry on 11/01/14. 9 | * 10 | * The a is the value in the monad and w is the log value (a monoid) 11 | */ 12 | @TypeChecked 13 | class WriterM { 14 | 15 | A value 16 | W log 17 | F2 plus 18 | 19 | WriterM(A a, W w, F2 f) { 20 | value = a 21 | log = w 22 | plus = f 23 | } 24 | 25 | static WriterM lift(B b, W w, F2 f) { 26 | new WriterM(b, w, f) 27 | } 28 | 29 | def WriterM tell(W w) { 30 | WriterM.lift(value, plus.f(log, w), plus) 31 | } 32 | 33 | def WriterM map(F f) { 34 | WriterM.lift(f.f(value), log, plus) 35 | } 36 | 37 | def WriterM flatMap(F> f) { 38 | def writer = f.f(value) 39 | WriterM.lift(writer.value, plus.f(log, writer.log), plus) 40 | } 41 | 42 | static WriterM lift(B b) { 43 | WriterM.lift(b, STRING_EMPTY, STRING_CONCAT) 44 | } 45 | 46 | static WriterM log(A a) { 47 | WriterM.lift(a, LOG_FUNCTION.f(a), STRING_CONCAT) 48 | } 49 | 50 | static F> log() { 51 | { A a -> WriterM.lift(a, LOG_FUNCTION.f(a), STRING_CONCAT) } as F 52 | } 53 | 54 | static F LOG_FUNCTION = { Object o -> "Added $o to the log\n"} as F 55 | static F2 STRING_CONCAT = {String a, String b -> a + b} as F2 56 | static String STRING_EMPTY = "" 57 | 58 | } 59 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/Yield.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import groovy.transform.TypeChecked 4 | import groovy.transform.Canonical 5 | 6 | /** 7 | * Created with IntelliJ IDEA. 8 | * User: MarkPerry 9 | * Date: 24/02/13 10 | * Time: 9:54 PM 11 | * To change this template use File | Settings | File Templates. 12 | */ 13 | //@TypeChecked 14 | @Canonical 15 | @TypeChecked 16 | class Yield { 17 | 18 | Map values = [:] 19 | Closure closure 20 | 21 | Object propertyMissing(String name) { 22 | values[name] 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/test/DbcContractValidator.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test 2 | 3 | import fj.F 4 | import fj.data.Validation 5 | import groovy.transform.TypeChecked 6 | import org.gcontracts.AssertionViolation 7 | import org.gcontracts.ClassInvariantViolation 8 | import org.gcontracts.PostconditionViolation 9 | import org.gcontracts.PreconditionViolation 10 | 11 | /** 12 | * Created with IntelliJ IDEA. 13 | * User: mwp 14 | * Date: 3/12/13 15 | * Time: 11:47 AM 16 | * To change this template use File | Settings | File Templates. 17 | */ 18 | @TypeChecked 19 | class DbcContractValidator { 20 | 21 | static F, Boolean> validateValidation() { 22 | Model.validator(validateThrowable()) 23 | } 24 | 25 | static F validateThrowable() { 26 | { Throwable t -> 27 | contractsOk(t) 28 | } as F 29 | } 30 | 31 | static Boolean contractViolation(Throwable t) { 32 | hasType(t, AssertionViolation.class) 33 | } 34 | 35 | static Boolean contractsOk(Throwable t) { 36 | invariantOk(t).and(preOk(t).implies(postOk(t))) 37 | } 38 | 39 | static Boolean preOk(Throwable t) { 40 | !hasType(t, PreconditionViolation.class) 41 | } 42 | 43 | static Boolean postOk(Throwable t) { 44 | !hasType(t, PostconditionViolation.class) 45 | } 46 | 47 | static Boolean invariantOk(Throwable t) { 48 | !hasType(t, ClassInvariantViolation.class) 49 | } 50 | 51 | static Boolean hasType(Object o, Class c) { 52 | c.isAssignableFrom(o.getClass()) 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /main/src/main/groovy/com/github/mperry/fg/test/Model.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test 2 | 3 | import fj.F 4 | import fj.data.Option 5 | import fj.data.Validation 6 | import fj.test.Arbitrary 7 | import groovy.transform.Canonical 8 | import groovy.transform.TypeChecked 9 | 10 | import static fj.test.Arbitrary.* 11 | 12 | /** 13 | * Created with IntelliJ IDEA. 14 | * User: MarkPerry 15 | * Date: 1/12/13 16 | * Time: 12:49 PM 17 | * To change this template use File | Settings | File Templates. 18 | */ 19 | @Canonical 20 | //@Immutable 21 | @TypeChecked 22 | class Model { 23 | 24 | static final Map NULLABLE_INTEGER = [(Integer.class): Arbitrary.arbNullableInteger()] 25 | 26 | static final Map, Arbitrary> DEFAULT_MAP = [ 27 | // basic generators 28 | (BigDecimal.class): arbBigDecimal, 29 | (BigInteger.class): arbBigInteger, 30 | (BitSet.class): arbBitSet, 31 | (Boolean.class): arbBoolean, 32 | (Byte.class): arbByte, 33 | (Calendar.class): arbCalendar, 34 | (Character.class): arbCharacterBoundaries, 35 | (Date.class): arbDate, 36 | (Double.class): arbDoubleBoundaries, 37 | (Float.class): arbFloatBoundaries, 38 | (Integer.class): arbIntegerBoundaries, 39 | (Long.class): arbLongBoundaries, 40 | (String.class): arbString, 41 | 42 | // more complex generators 43 | (ArrayList.class): arbArrayList(arbIntegerBoundaries), 44 | (java.util.List.class): arbArrayList(arbIntegerBoundaries), 45 | (fj.data.List.class): arbList(arbIntegerBoundaries) 46 | ] 47 | 48 | static final F, Boolean> DEFAULT_VALIDATOR = { Validation v -> 49 | v.isFail() ? false : v.success() 50 | } as F 51 | 52 | Map, Arbitrary> map = DEFAULT_MAP 53 | Closure function 54 | Option> pre = Option.none() 55 | // Option> pre = Option.some({ -> true}) 56 | Boolean truth = true 57 | F, Boolean> validator = DEFAULT_VALIDATOR 58 | 59 | Model addArbs(Map, Arbitrary> m) { 60 | new Model(map + m, function, pre, truth) 61 | } 62 | 63 | @TypeChecked 64 | static F, Boolean> validator(F f) { 65 | { Validation v -> 66 | v.isFail() ? f.f(v.fail()) : v.success() 67 | } as F 68 | } 69 | 70 | } 71 | 72 | -------------------------------------------------------------------------------- /main/src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule: -------------------------------------------------------------------------------- 1 | moduleName=functionalgroovy-main 2 | moduleVersion=@version@ 3 | extensionClasses=com.github.mperry.fg.SimpleIOExtension,com.github.mperry.fg.ListMonadExtension,com.github.mperry.fg.StreamExtension2,com.github.mperry.fg.SqlExtension,com.github.mperry.fg.SetExtension2,com.github.mperry.fg.CollectionExtension2,com.github.mperry.fg.SetMonadExtension,com.github.mperry.fg.FutureExtension 4 | staticExtensionClasses=com.github.mperry.fg.SimpleIOStaticExtension,com.github.mperry.fg.ListMonadStaticExtension,com.github.mperry.fg.StreamStaticExtension2,com.github.mperry.fg.SetStaticExtension2,com.github.mperry.fg.CollectionStaticExtension2,com.github.mperry.fg.SetMonadStaticExtension 5 | 6 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/ComprehensionTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.data.Option 4 | import org.junit.Test 5 | 6 | import static Comprehension.foreach 7 | import static fj.data.Option.* 8 | import static junit.framework.Assert.assertTrue 9 | 10 | /** 11 | * Created with IntelliJ IDEA. 12 | * User: MarkPerry 13 | * Date: 26/02/13 14 | * Time: 12:39 AM 15 | * To change this template use File | Settings | File Templates. 16 | */ 17 | class ComprehensionTest { 18 | 19 | @Test 20 | void simple() { 21 | def res = foreach { 22 | a { 1.to(2) } 23 | yield { 24 | a + 1 25 | } 26 | } 27 | def expected = [2, 3] 28 | def actual = res.toJList() 29 | assertTrue(expected == actual) 30 | } 31 | 32 | @Test 33 | void simpleLists() { 34 | def res = foreach { 35 | a { 36 | [1, 2] 37 | } 38 | yield { 39 | a + 1 40 | } 41 | } 42 | def expected = [2, 3] 43 | def actual = res 44 | assertTrue(expected == actual) 45 | } 46 | 47 | @Test 48 | void simpleListsWithShift() { 49 | def res = foreach { 50 | a << [1, 2] 51 | yield { 52 | a + 1 53 | } 54 | } 55 | def expected = [2, 3] 56 | def actual = res 57 | assertTrue(expected == actual) 58 | } 59 | 60 | @Test 61 | void test1() { 62 | def res = foreach { 63 | a { 1.to(2) } 64 | b { 1.to(1) } 65 | yield { 66 | [a, b] 67 | } 68 | } 69 | // def expected = [[1, 3], [1, 4], [2, 3], [2, 4]] 70 | def expected = [[1, 1], [2, 1]] 71 | assertTrue(expected == res.toJList()) 72 | } 73 | 74 | @Test 75 | void test2() { 76 | def res = foreach { 77 | a { 1.to(2) } 78 | b { a.to(2) } 79 | yield { 80 | [a, b] 81 | } 82 | } 83 | def expected = [[1, 1], [1, 2], [2, 2]] 84 | def actual = res.toJList() 85 | assertTrue(expected == actual) 86 | } 87 | 88 | @Test 89 | void test3() { 90 | def res = foreach { 91 | a { 1.to(2) } 92 | guard { 93 | a == 2 94 | } 95 | yield { 96 | a 97 | } 98 | } 99 | def expected = [2] 100 | assertTrue(expected == res.toJList()) 101 | } 102 | 103 | @Test 104 | void test4() { 105 | def res = foreach { 106 | a { 1.to(2) } 107 | b { 3.to(4) } 108 | guard { 109 | a == 2 && b == 3 110 | } 111 | c { 5.to(6) } 112 | guard { c == 5 } 113 | yield { 114 | [a, b, c] 115 | } 116 | } 117 | def expected = [[2, 3, 5]] 118 | def actual = res.toJList() 119 | assertTrue(actual == expected) 120 | } 121 | 122 | @Test 123 | void test5() { 124 | def res = foreach { 125 | a << [some(0), some(1), some(2), none(), some(10)] 126 | guard { 127 | a.filter { 128 | it > 1 129 | }.isSome() 130 | } 131 | yield { 132 | a.map { it + 3 } 133 | } 134 | } 135 | def expected = [ some(5), some(13)] 136 | println res 137 | assertTrue (res == expected) 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/FTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.F1Functions 5 | import groovy.transform.TypeChecked 6 | import org.junit.Test 7 | 8 | import static org.junit.Assert.assertTrue 9 | 10 | /** 11 | * Created with IntelliJ IDEA. 12 | * User: MarkPerry 13 | * Date: 20/12/13 14 | * Time: 8:52 AM 15 | * To change this template use File | Settings | File Templates. 16 | */ 17 | @TypeChecked 18 | class FTest { 19 | 20 | @Test 21 | void test1() { 22 | def e = F.unit{Integer i -> i + 1} 23 | def f = F1Functions.o(e, {Integer i -> i * 3} as F) 24 | def g = F.unit{Integer i -> i + 1} 25 | def h = F1Functions.o(g, {Integer i -> i * 3} as F) 26 | assertTrue(4 == f.f(1)) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/IdentityTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.Unit 5 | import fj.test.Arbitrary 6 | import fj.test.Coarbitrary 7 | import groovy.transform.TypeChecked 8 | import groovy.transform.TypeCheckingMode 9 | import org.junit.Test 10 | 11 | import static fj.test.Arbitrary.* 12 | import static fj.test.Coarbitrary.* 13 | 14 | /** 15 | * Created by MarkPerry on 9/01/14. 16 | */ 17 | @TypeChecked 18 | class IdentityTest { 19 | 20 | IdentityM monad() { 21 | IdentityM.idUnit() 22 | } 23 | 24 | @TypeChecked(TypeCheckingMode.SKIP) 25 | def Arbitrary> arbId(Arbitrary aa) { 26 | arbitrary(aa.gen.map({ A a -> IdentityM.lift(a) } as F)) 27 | } 28 | 29 | Arbitrary> arbIdInteger() { 30 | arbId(arbInteger) 31 | } 32 | 33 | @Test 34 | void leftIdentity() { 35 | def f = arbF(coarbInteger, arbIdInteger()) 36 | new MonadLaws().leftIdentity(monad(), f, arbInteger) 37 | } 38 | 39 | @Test 40 | void rightIdentity() { 41 | new MonadLaws().rightIdentity(monad(), arbIdInteger()) 42 | } 43 | 44 | @Test 45 | @TypeChecked(TypeCheckingMode.SKIP) 46 | void associativity() { 47 | new MonadLaws().associativity(monad(), arbIdInteger(), arbF(coarbInteger, arbId(arbString)), arbF(Coarbitrary.coarbString, arbId(arbLong))) 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/IdentityTest2.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.Unit 5 | import groovy.transform.TypeChecked 6 | import groovy.transform.TypeCheckingMode 7 | import org.junit.Test 8 | 9 | import static IdentityM.lift 10 | import static junit.framework.Assert.assertTrue 11 | 12 | /** 13 | * Created by MarkPerry on 18/04/2014. 14 | */ 15 | @TypeChecked 16 | class IdentityTest2 { 17 | 18 | IdentityM monad() { 19 | IdentityM.idUnit() 20 | } 21 | 22 | @Test 23 | void join() { 24 | def n = 3 25 | assertTrue(monad().join(lift(lift(n))) == lift(n)) 26 | } 27 | 28 | @Test 29 | void map() { 30 | def a = 2 31 | def b = 3 32 | def f = { Integer i -> a * i } as F 33 | assertTrue(monad().map(lift(b), f) == lift(a * b)) 34 | } 35 | 36 | @Test 37 | void map2() { 38 | assertTrue(monad().map2(lift(2), lift(3), { Integer a, Integer b -> a * b}) == lift(6)) 39 | } 40 | 41 | @Test 42 | void to() { 43 | assertTrue(monad().to(lift(3), "abc") == lift("abc")) 44 | } 45 | 46 | @Test 47 | @TypeChecked(TypeCheckingMode.SKIP) 48 | void foldM() { 49 | assertTrue(monad().foldM(1.to(5), 1.toInteger(), { Integer a, Integer b -> lift(a * b)}) == lift(120)) 50 | } 51 | 52 | @Test 53 | void sequence() { 54 | assertTrue(monad().sequence([lift(2), lift(3), lift(4)]) == lift([2, 3, 4])) 55 | } 56 | 57 | @Test 58 | @TypeChecked(TypeCheckingMode.SKIP) 59 | void traverse() { 60 | F> f = { Integer i -> lift(i + 1) } as F 61 | // as F> 62 | // as F 63 | // as F> 64 | def actual = monad().traverse([2, 3, 4], f) 65 | def expected = lift([3, 4, 5]) 66 | assertTrue(actual == expected) 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/ListMonadTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import com.github.mperry.fg.typeclass.Monad 4 | import com.github.mperry.fg.typeclass.concrete.ListMonad 5 | import groovy.transform.TypeChecked 6 | import groovy.transform.TypeCheckingMode 7 | import org.junit.Test 8 | 9 | import static com.github.mperry.fg.test.ArbitraryCompanion.* 10 | import static fj.test.Arbitrary.arbF 11 | import static fj.test.Arbitrary.* 12 | import static fj.test.Coarbitrary.* 13 | 14 | /** 15 | * Created by MarkPerry on 10/01/14. 16 | */ 17 | @TypeChecked 18 | class ListMonadTest { 19 | 20 | Monad monad() { 21 | new ListMonad() 22 | } 23 | 24 | @Test 25 | @TypeChecked(TypeCheckingMode.SKIP) 26 | void leftIdentity() { 27 | def f = arbF(coarbInteger, arbJavaList(arbInteger)) 28 | new MonadLaws().leftIdentity(monad(), f, arbInteger) 29 | } 30 | 31 | @Test 32 | void rightIdentity() { 33 | new MonadLaws().rightIdentity(monad(), arbJavaList(arbInteger)) 34 | } 35 | 36 | @Test 37 | @TypeChecked(TypeCheckingMode.SKIP) 38 | void associativity() { 39 | new MonadLaws().associativity(monad(), arbJavaList(arbInteger), arbFInvariant(arbJavaList(arbString)), arbFInvariant(arbJavaList(arbLong))) 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/ListTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import groovy.transform.TypeChecked 5 | import org.junit.Test 6 | 7 | import static junit.framework.Assert.assertTrue 8 | 9 | /** 10 | * Created by MarkPerry on 16/01/14. 11 | */ 12 | @TypeChecked 13 | class ListTest { 14 | 15 | @Test 16 | void flatMap() { 17 | 18 | } 19 | 20 | @Test 21 | void join() { 22 | // assertTrue(List.join([[1, 2], [3, 4], []]) == [1, 2, 3, 4]) 23 | } 24 | 25 | @Test 26 | void map() { 27 | // assertTrue([1, 2, 3].map({ Integer i -> i * 2} as F) == [2, 4, 6]) 28 | } 29 | 30 | // @Test 31 | // void map2() { 32 | // assertTrue([1, 2, 3].map2([3, 4, 5], {Integer i, Integer j -> i * j} as F) == [3, 8, 15]) 33 | // } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/MonadLaws.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import com.github.mperry.fg.typeclass.Monad 4 | import fj.F 5 | import fj.F2 6 | import fj.F3 7 | import fj.test.Arbitrary 8 | import groovy.transform.TypeChecked 9 | import groovy.transform.TypeCheckingMode 10 | 11 | import static fj.test.Property.prop 12 | import static fj.test.Property.property 13 | 14 | /** 15 | * Created by MarkPerry on 9/01/14. 16 | */ 17 | @TypeChecked 18 | class MonadLaws { 19 | 20 | // Left identity: return a >>= f == f a 21 | @TypeChecked(TypeCheckingMode.SKIP) 22 | def void leftIdentity(Monad m, Arbitrary>> af, Arbitrary aa) { 23 | def p = property(af, aa, { 24 | F> f, A a -> 25 | def b = m.flatMap(m.unit(a), f).equals(f.f(a)) 26 | prop(b) 27 | } as F2) 28 | p.checkOkWithSummary() 29 | } 30 | 31 | // Right identity: m >>= return == m 32 | @TypeChecked(TypeCheckingMode.SKIP) 33 | def void rightIdentity(Monad monad, Arbitrary> arb) { 34 | def p = property(arb, { 35 | M m -> 36 | prop(monad.flatMap(m, monad.unit()).equals(m)) 37 | // def u = monad.unit() 38 | // def m1 = monad.flatMap(m, u) 39 | // def b = m1.equals(m) 40 | // prop(b) 41 | } as F) 42 | p.checkOkWithSummary() 43 | } 44 | 45 | // Associativity: (m >>= f) >>= g == m >>= (\x -> f x >>= g) 46 | @TypeChecked(TypeCheckingMode.SKIP) 47 | def void associativity(Monad m, Arbitrary> ama, Arbitrary>> af1, Arbitrary>> af2) { 48 | def p = property(ama, af1, af2, { 49 | M o, F> f, F> g -> 50 | prop(m.flatMap(m.flatMap(o, f), g).equals(m.flatMap(o, { A i -> m.flatMap(f.f(i), g) } as F))) 51 | } as F3) 52 | p.checkOkWithSummary() 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/ObjectTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F2 4 | import groovy.transform.TypeChecked 5 | import org.junit.Before 6 | import org.junit.Test 7 | 8 | import javax.management.relation.RoleList 9 | 10 | import static junit.framework.Assert.assertTrue 11 | 12 | /** 13 | * Created by MarkPerry on 15/01/14. 14 | */ 15 | @TypeChecked 16 | class ObjectTest { 17 | 18 | List list 19 | 20 | @Before 21 | void setUp() { 22 | list = new ArrayList<>() 23 | list.addAll([3, 4]) 24 | } 25 | 26 | def void checkCompatibility(List classes, A o, F2 f, Boolean truth) { 27 | classes.each { Class clazz -> 28 | assertTrue(f.f(o, clazz) == truth) 29 | } 30 | } 31 | 32 | def void checkCompatibility(List good, List bad, A a, F2 f) { 33 | checkCompatibility(good, a, f, true) 34 | checkCompatibility(bad, a, f, false) 35 | } 36 | 37 | @Test 38 | void subInstance() { 39 | def ok = [List.class, ArrayList.class, Collection.class] 40 | def bad = [RoleList.class] 41 | checkCompatibility(ok, bad, list, { Object o, Class c -> o.isSubInstanceOf(c)} as F2) 42 | } 43 | 44 | @Test 45 | void direct() { 46 | def ok = [ArrayList.class] 47 | def bad = [RoleList.class, List.class, Collection.class] 48 | checkCompatibility(ok, bad, list, { Object o, Class c -> o.isDirectInstanceOf(c)} as F2) 49 | } 50 | 51 | @Test 52 | void properSubInstance() { 53 | def ok = [List.class, Collection.class] 54 | def bad = [RoleList.class, ArrayList.class] 55 | checkCompatibility(ok, bad, list, { Object o, Class c -> o.isProperSubInstanceOf(c)} as F2) 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/ReaderTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import org.junit.Test 5 | 6 | import static com.github.mperry.fg.Comprehension.foreach 7 | import static junit.framework.Assert.assertTrue 8 | 9 | /** 10 | * Created by MarkPerry on 11/01/14. 11 | */ 12 | class ReaderTest { 13 | 14 | @Test 15 | void test1() { 16 | def f = { Integer i -> i * 2} as F 17 | def g = { Integer i -> i + 10} as F 18 | def r = ReaderM.lift(f).flatMap({ Integer a -> ReaderM.lift({ Integer i -> i + 10 } as F) 19 | .map({Integer b -> a + b } as F)} as F) 20 | def z = r.f(3) 21 | println z 22 | assertTrue(z == 19) 23 | } 24 | 25 | @Test 26 | void test2() { 27 | def r = foreach { 28 | a << ReaderM.lift({ Integer i -> i * 2} as F) 29 | b << ReaderM.lift({ Integer i -> i + 10} as F) 30 | yield { a + b } 31 | } 32 | assertTrue(r.f(3) == 19) 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/SimpleIOTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import org.junit.Test 4 | 5 | /** 6 | * Created with IntelliJ IDEA. 7 | * User: MarkPerry 8 | * Date: 7/11/13 9 | * Time: 4:51 PM 10 | * To change this template use File | Settings | File Templates. 11 | */ 12 | class SimpleIOTest { 13 | 14 | @Test 15 | void test1() { 16 | def a = IOConstants.stdinReadLine() 17 | // def b = a.run() 18 | // println "Got from console: $b" 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/StackOverflowTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.data.Stream 5 | import groovy.transform.TypeChecked 6 | import groovy.transform.TypeCheckingMode 7 | import org.junit.Test 8 | 9 | /** 10 | * Created by MarkPerry on 20/12/13. 11 | */ 12 | @TypeChecked 13 | class StackOverflowTest { 14 | 15 | @Test 16 | @TypeChecked(TypeCheckingMode.SKIP) 17 | void testRecursive() { 18 | // stack overflow with about 10000 elements 19 | def stream = 1.to(1000).map({SimpleIO.lift("mark$it")} as F) 20 | def singleIo = SimpleIO.sequenceWhileR(stream, {String s -> true} as F) 21 | def result = singleIo.run().toList().toJavaList() 22 | // def show = result.toString() 23 | // println "result: $show" 24 | } 25 | 26 | @Test 27 | @TypeChecked(TypeCheckingMode.SKIP) 28 | void test2() { 29 | // stackoverflow with ~10000 elements 30 | def smallStream = 1.to(1000).map({SimpleIO.lift("mark$it")} as F) 31 | def tramp = SimpleIO.sequenceWhileC(smallStream, {String s -> true} as F) 32 | def io2 = tramp.run() 33 | def result = io2.run() 34 | // println "result: ${result.toList()}" 35 | } 36 | 37 | @Test 38 | @TypeChecked(TypeCheckingMode.SKIP) 39 | void testLoop() { 40 | def stream = 1.to(10000).map({SimpleIO.lift("mark$it")} as F) 41 | def singleIo = SimpleIO.sequenceWhile(stream, {String s -> true} as F) 42 | def result = singleIo.run().toList().toJavaList() 43 | // def show = result.toString() 44 | // println "result: $show" 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/StateIntDynamicMonadTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import com.github.mperry.fg.typeclass.Monad 4 | import fj.F 5 | import fj.test.Arbitrary 6 | import groovy.transform.TypeChecked 7 | import groovy.transform.TypeCheckingMode 8 | import org.junit.Test 9 | 10 | import static fj.test.Arbitrary.arbF 11 | import static fj.test.Arbitrary.arbInteger 12 | import static fj.test.Arbitrary.arbLong 13 | import static fj.test.Arbitrary.arbString 14 | import static fj.test.Arbitrary.arbitrary 15 | import static fj.test.Coarbitrary.coarbInteger 16 | import static fj.test.Coarbitrary.coarbString 17 | 18 | /** 19 | * Created by MarkPerry on 10/01/14. 20 | */ 21 | //@TypeChecked 22 | class StateIntDynamicMonadTest { 23 | 24 | def loader = lambda().newLoader() 25 | def stateClass = Integer.class 26 | def partialState = partialState(stateClass) 27 | 28 | TypeLambda lambda() { 29 | new TypeLambda() 30 | } 31 | 32 | Class partialState(Class clazz) { 33 | lambda().partialStateApplication(loader, clazz) 34 | } 35 | 36 | Class> monadClass() { 37 | lambda().stateMonad(loader, partialState, stateClass) 38 | } 39 | 40 | Monad monad() { 41 | monadClass().newInstance() 42 | } 43 | 44 | @TypeChecked(TypeCheckingMode.SKIP) 45 | def Arbitrary arbState(Arbitrary aa) { 46 | arbState(aa, stateClass) 47 | } 48 | 49 | @TypeChecked(TypeCheckingMode.SKIP) 50 | def Arbitrary arbState(Arbitrary aa, Class stateType) { 51 | def text = "{${stateType.canonicalName} s -> P.p(a, s)}" 52 | def sh = new GroovyShell(loader) 53 | def f = sh.evaluate(text) 54 | arbitrary(aa.gen.map({ A a -> partialState.newInstance(f as F)} as F)) 55 | } 56 | 57 | Arbitrary arbStateInt() { 58 | arbState(arbInteger) 59 | } 60 | 61 | @Test 62 | void leftIdentity() { 63 | new MonadLaws().leftIdentity(monad(), arbF(coarbInteger, arbStateInt()), arbInteger) 64 | } 65 | 66 | @Test 67 | void rightIdentity() { 68 | new MonadLaws().rightIdentity(monad(), arbStateInt()) 69 | } 70 | 71 | @Test 72 | void associativity() { 73 | new MonadLaws().associativity(monad(), arbStateInt(), arbF(coarbInteger, arbState(arbString)), 74 | arbF(coarbString, arbState(arbLong))) 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/StateIntMonadTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import fj.P 5 | import fj.Unit 6 | import fj.test.Arbitrary 7 | import fj.test.Coarbitrary 8 | import fj.test.Gen 9 | import groovy.transform.TypeChecked 10 | import groovy.transform.TypeCheckingMode 11 | import org.junit.Test 12 | 13 | import static fj.test.Arbitrary.arbF 14 | import static fj.test.Arbitrary.arbF 15 | import static fj.test.Arbitrary.arbF 16 | import static fj.test.Arbitrary.arbInteger 17 | import static fj.test.Arbitrary.arbInteger 18 | import static fj.test.Arbitrary.arbLong 19 | import static fj.test.Arbitrary.arbString 20 | import static fj.test.Arbitrary.arbitrary 21 | import static fj.test.Coarbitrary.coarbInteger 22 | import static fj.test.Coarbitrary.* 23 | 24 | /** 25 | * Created by MarkPerry on 9/01/14. 26 | */ 27 | @TypeChecked 28 | class StateIntMonadTest { 29 | 30 | StateIntMonad monad() { 31 | new StateIntMonad() 32 | } 33 | 34 | @TypeChecked(TypeCheckingMode.SKIP) 35 | def Arbitrary> arbState(Arbitrary aa) { 36 | arbitrary(aa.gen.map({ A a -> new StateInt({ Integer i -> P.p(a, i) } as F)} as F)) 37 | } 38 | 39 | Arbitrary> arbStateInt() { 40 | arbState(arbInteger) 41 | } 42 | 43 | @Test 44 | void leftIdentity() { 45 | new MonadLaws().leftIdentity(monad(), arbF(coarbInteger, arbStateInt()), arbInteger) 46 | } 47 | 48 | @Test 49 | void rightIdentity() { 50 | new MonadLaws().rightIdentity(monad(), arbStateInt()) 51 | } 52 | 53 | @Test 54 | @TypeChecked(TypeCheckingMode.SKIP) 55 | void associativity() { 56 | new MonadLaws().associativity(monad(), arbStateInt(), arbF(coarbInteger, arbState(arbString)), 57 | arbF(coarbString, arbState(arbLong))) 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/StreamStaticExtension2Test.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.P 4 | import fj.P2 5 | import fj.data.Option 6 | import fj.data.Stream 7 | import groovy.transform.TypeChecked 8 | import org.junit.Test 9 | 10 | import static fj.P.p 11 | import static fj.data.Option.some 12 | import static org.junit.Assert.assertTrue 13 | 14 | /** 15 | * Created by MarkPerry on 21/01/14. 16 | */ 17 | @TypeChecked 18 | class StreamStaticExtension2Test { 19 | 20 | @Test 21 | void fib() { 22 | def s = Stream.unfold(p(1, 1)) { P2 p -> 23 | def low = p._1() 24 | def high = p._2() 25 | some(P.p(low, P.p(high, low + high))) 26 | } 27 | def list = s.take(10).toJavaList() 28 | def expected = [1, 1, 2, 3, 5, 8, 13, 21, 34, 55] 29 | assertTrue(list == expected) 30 | } 31 | 32 | 33 | } 34 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/StreamTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.P1 4 | import groovy.transform.TypeChecked 5 | import groovy.transform.TypeCheckingMode 6 | import org.junit.Test 7 | import fj.data.Stream 8 | 9 | import static org.junit.Assert.assertTrue 10 | import fj.F 11 | import fj.F2 12 | //import com.github.mperry.fg.StreamExtension 13 | 14 | /** 15 | * Created with IntelliJ IDEA. 16 | * User: MarkPerry 17 | * Date: 20/02/13 18 | * Time: 2:03 AM 19 | * To change this template use File | Settings | File Templates. 20 | */ 21 | @TypeChecked 22 | class StreamTest { 23 | 24 | @Test 25 | void simple() { 26 | def s = Stream.range(1) 27 | def s2 = s.filter { Integer it -> it % 2 == 0}.take(4) 28 | assertTrue(s2.toJList() == [2, 4, 6, 8]) 29 | } 30 | 31 | @Test 32 | void combos2() { 33 | def a = 1.to(2).combos(3.to(4)) 34 | def expected = [[1, 3], [1, 4], [2, 3], [2, 4]] 35 | assertTrue(a.toJList() == expected) 36 | } 37 | 38 | @Test 39 | void combos3() { 40 | def b = 1.to(2).combos(3.to(4)).combos(5.to(6)) 41 | def expected = [[1, 3, 5], [1, 3, 6], [1, 4, 5], [1, 4, 6],[2, 3, 5], [ 2, 3, 6], [2, 4, 5], [2, 4, 6]] 42 | assertTrue(b.toJList() == expected) 43 | } 44 | 45 | @Test 46 | void combos4() { 47 | def b = 1.to(2).combos(3.to(4)).combos(5.to(6)).combos(7.to(8)) 48 | def expected = [[1, 3, 5, 7], [1, 3, 5, 8], [1, 3, 6, 7], [1, 3, 6, 8], [1, 4, 5, 7], [1, 4, 5, 8], 49 | [1, 4, 6, 7], [1, 4, 6, 8], [2, 3, 5, 7], [2, 3, 5, 8], [2, 3, 6, 7], [2, 3, 6, 8], 50 | [2, 4, 5, 7], [2, 4, 5, 8], [2, 4, 6, 7], [2, 4, 6, 8]] 51 | assertTrue(b.toJList() == expected) 52 | } 53 | 54 | @Test 55 | void mapWithSubstreams() { 56 | def b = 1.to(2).map{ Integer it -> it.to(4)} 57 | assertTrue(b.toJList() == [[1, 2, 3, 4], [2, 3, 4]]) 58 | } 59 | 60 | @Test 61 | void simpleToJList() { 62 | def a = 1.to(3) 63 | assertTrue(a.toJList() == [1, 2, 3]) 64 | } 65 | 66 | @Test 67 | @TypeChecked(TypeCheckingMode.SKIP) 68 | void fold() { 69 | def s = 1.to(5) 70 | def f2 = { Integer x -> { Integer y -> x + y } as F } as F 71 | def a = s.foldLeft(f2, 0) 72 | def b = s.foldLeft({Integer x, Integer y -> x + y} as F2, 0) 73 | def c = s.foldLeft({Integer x, Integer y -> x + y} as F2, 0) 74 | assertTrue(a == 15) 75 | assertTrue(b == 15) 76 | assertTrue(c == 15) 77 | } 78 | 79 | // @Test 80 | @TypeChecked(TypeCheckingMode.SKIP) 81 | void streamFlatMapOverflows() { 82 | def max = 1000000 83 | def p = { -> 84 | def s = 1.to(max).map { Integer i -> 85 | [i, i + 1] 86 | }.flatMap { List list -> 87 | Stream.stream(list[0], list[1]) 88 | } 89 | def list = s.toJavaList() 90 | println list 91 | } as P1 92 | assertTrue(p.throwsError(StackOverflowError.class)) 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/WriterTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg 2 | 3 | import fj.F 4 | import groovy.transform.TypeChecked 5 | import groovy.transform.TypeCheckingMode 6 | import org.junit.Test 7 | 8 | import static com.github.mperry.fg.Comprehension.foreach 9 | import static WriterM.getLOG_FUNCTION 10 | import static junit.framework.Assert.assertTrue 11 | 12 | /** 13 | * Created by MarkPerry on 11/01/14. 14 | */ 15 | @TypeChecked 16 | class WriterTest { 17 | 18 | @Test 19 | void test1() { 20 | def w = WriterM.log(3).flatMap({ Integer a -> WriterM.log(5).map({Integer b -> a * b} as F)} as F) 21 | println "writer: $w, value: ${w.value} log: ${w.log}" 22 | assertTrue(w.value == 15) 23 | assertTrue(w.log == LOG_FUNCTION.f(3) + LOG_FUNCTION.f(5)) 24 | } 25 | 26 | @Test 27 | @TypeChecked(TypeCheckingMode.SKIP) 28 | void test2() { 29 | def w = foreach { 30 | a << WriterM.log(3) 31 | b << { WriterM.log(5) } 32 | yield { a * b } 33 | } 34 | assertTrue(w.value == 15) 35 | assertTrue(w.log == LOG_FUNCTION.f(3) + LOG_FUNCTION.f(5)) 36 | 37 | 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/euler/P01.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.euler 2 | 3 | import fj.F2 4 | import groovy.transform.TypeChecked 5 | import junit.framework.TestCase 6 | import org.junit.Test 7 | 8 | /* 9 | * If we list all the natural numbers below 10 that are multiples of 3 or 5, 10 | * we get 3, 5, 6 and 9. The sum of these multiples is 23. 11 | * 12 | * Find the sum of all the multiples of 3 or 5 below 1000. 13 | * 14 | */ 15 | @TypeChecked 16 | class P01 extends GroovyTestCase { 17 | 18 | def f(int min, int max) { 19 | min.to(max).filter { Integer it -> 20 | it % 3 == 0 || it % 5 == 0 21 | }.foldLeft({ Integer acc, Integer val -> 22 | acc + val 23 | } as F2, 0) 24 | } 25 | 26 | @Test 27 | void test1() { 28 | assertTrue(f(1, 9) == 23) 29 | assertTrue(f(1, 10 ** 3 - 1) == 233168) 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/euler/P02.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.euler 2 | 3 | import fj.F1Functions 4 | 5 | //import com.github.mperry.fg.Functions 6 | import fj.F2 7 | import fj.Function 8 | import fj.data.Stream 9 | import groovy.transform.TypeChecked 10 | import groovy.transform.TypeCheckingMode 11 | import org.junit.Test 12 | 13 | /* 14 | * Each new term in the Fibonacci sequence is generated by adding the previous two 15 | * terms. By starting with 1 and 2, the first 10 terms will be: 16 | * 17 | * 1, 2, 3, 5, 8, 13, 21, 34, 55, 89, ... 18 | * 19 | * By considering the terms in the Fibonacci sequence whose values do not exceed 20 | * four million, find the sum of the even-valued terms. 21 | * 22 | */ 23 | //@TypeChecked 24 | @TypeChecked(TypeCheckingMode.SKIP) 25 | class P02 extends GroovyTestCase { 26 | 27 | Stream fibMethodUnchecked(int a, int b) { 28 | Stream.cons(a, (this.&fibMethodChecked as F2).curry().f(b).lazy().f(a + b)) 29 | } 30 | 31 | @TypeChecked 32 | Stream fibMethodChecked(int a, int b) { 33 | def f = (this.&fibMethodChecked as F2>) 34 | def g = Function.curry(f).f(b) 35 | Stream.cons(a, F1Functions.lazy(g).f(a + b)) 36 | } 37 | 38 | Closure> fibCUnchecked = { int a, int b -> 39 | Stream.cons(a, (fibCUnchecked as F2).curry().f(b).lazy().f(a + b)) 40 | } 41 | 42 | // @TypeChecked 43 | // Groovyc with TypeChecked returns error: Unimplemented node type 44 | Closure> fibCChecked = { int a, int b -> 45 | Stream.cons(a, (fibCChecked as F2>).curry().f(b).lazy().f(a + b)) 46 | } 47 | 48 | Stream fib() { 49 | fibMethodChecked(1, 2) 50 | } 51 | 52 | @Test 53 | @TypeChecked 54 | void test1() { 55 | def max = 4 * 10 ** 6 56 | def r = fib().takeWhile{ int it -> it < max }.filter { int it -> 57 | it % 2 == 0 58 | }.fold(0) { int acc, int v -> acc + v} 59 | println r 60 | assertTrue(r == 4613732) 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/euler/P03.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.euler 2 | 3 | import fj.data.Stream 4 | import groovy.transform.TypeChecked 5 | import groovy.transform.TypeCheckingMode 6 | import org.junit.Test 7 | 8 | /* 9 | * The prime factors of 13195 are 5, 7, 13 and 29. 10 | * 11 | * What is the largest prime factor of the number 600851475143 ? 12 | * 13 | */ 14 | //@TypeChecked 15 | @TypeChecked(TypeCheckingMode.SKIP) 16 | class P03 extends GroovyTestCase { 17 | 18 | Stream factors(BigInteger n) { 19 | factorsC(n, 1) 20 | } 21 | 22 | 23 | Stream factors(BigInteger numerator, BigInteger denominator) { 24 | numerator == 1 ? Stream.nil() : 25 | (numerator % denominator != 0) ? 26 | factors(numerator, denominator + 1) : 27 | factors((numerator / denominator).toBigInteger(), denominator + 1).cons(denominator) 28 | } 29 | 30 | Closure> factorsC = { BigInteger numerator, BigInteger denominator -> 31 | if (numerator == 1) { 32 | Stream.nil() 33 | } else if (numerator % denominator != 0) { 34 | factorsC.trampoline(numerator, denominator + 1) 35 | } else { 36 | def s = factorsC.trampoline().call((numerator / denominator).toBigInteger(), 2) 37 | s.cons(denominator) 38 | } 39 | }.trampoline() 40 | 41 | @Test 42 | void test() { 43 | // println factors(12).toList() 44 | println factors(13195).toList() 45 | def list = factors(600851475143).toList() 46 | println list 47 | assertTrue(6857 == list.last()) 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/euler/P04.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.euler 2 | 3 | import com.github.mperry.fg.Comprehension 4 | import fj.F2 5 | import fj.P 6 | import fj.P2 7 | import fj.data.Option 8 | import fj.data.Stream 9 | import groovy.transform.TypeChecked 10 | import groovy.transform.TypeCheckingMode 11 | import org.junit.Ignore 12 | import org.junit.Test 13 | 14 | /* 15 | * A palindromic number reads the same both ways. The largest palindrome made from 16 | * the product of two 2-digit numbers is 9009 = 91 * 99. 17 | * 18 | * Find the largest palindrome made from the product of two 3-digit numbers. 19 | * 20 | */ 21 | //@TypeChecked 22 | @TypeChecked(TypeCheckingMode.SKIP) 23 | class P04 extends GroovyTestCase { 24 | 25 | @TypeChecked 26 | P2 limits(int i) { 27 | P.p((i / 10).intValue(), i - 1) 28 | } 29 | 30 | @TypeChecked 31 | Stream p(int lim) { 32 | intStream(lim).filter { Integer i -> isPalindrome(i.toString()) } 33 | } 34 | 35 | Stream intStream(int limit) { 36 | Comprehension.foreach { 37 | def p = limits(limit) 38 | def s = p._1().to(p._2()) 39 | a << s 40 | b << {a.to(p._2())} 41 | yield { 42 | a * b 43 | } 44 | } 45 | } 46 | 47 | @TypeChecked 48 | Option palindrome(int a) { 49 | Option.some(a).filter { isPalindrome(it.toString()) } 50 | } 51 | 52 | @TypeChecked 53 | boolean isPalindrome(String s) { 54 | s.reverse().equals(s) 55 | } 56 | 57 | @TypeChecked 58 | int highest(int limit) { 59 | p(limit).foldLeft({ Integer acc, Integer val -> 60 | acc > val ? acc : val 61 | } as F2, 0) 62 | } 63 | 64 | @Test 65 | void testVeryLow() { 66 | highest(10) 67 | } 68 | 69 | @TypeChecked 70 | @Test 71 | void testLow() { 72 | assertTrue(highest(100) == 9009) 73 | } 74 | 75 | @TypeChecked 76 | @Test 77 | void testHigh() { 78 | def v = highest(1000) 79 | println v 80 | assertTrue(v == 906609) 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/euler/P05.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.euler 2 | 3 | import fj.F 4 | import fj.data.Enumerator 5 | import fj.data.Stream 6 | import groovy.transform.CompileStatic 7 | import groovy.transform.TypeChecked 8 | import groovy.transform.TypeCheckingMode 9 | import org.junit.Ignore 10 | import org.junit.Test 11 | 12 | /** 13 | * Created with IntelliJ IDEA. 14 | * User: MarkPerry 15 | * Date: 21/06/13 16 | * Time: 9:08 PM 17 | * To change this template use File | Settings | File Templates. 18 | */ 19 | 20 | 21 | /* 22 | * 2520 is the smallest number that can be divided by each of the numbers from 23 | * 1 to 10 without any remainder. 24 | * 25 | * What is the smallest positive number that is evenly divisible by all of the 26 | * numbers from 1 to 20? 27 | * 28 | */ 29 | 30 | //@TypeChecked 31 | @TypeChecked(TypeCheckingMode.SKIP) 32 | class P05 extends GroovyTestCase { 33 | 34 | boolean divisible(int num, int min, int max) { 35 | divisible1(num, min, max) 36 | } 37 | 38 | @CompileStatic 39 | @TypeChecked 40 | boolean divisible1(int num, int min, int max) { 41 | def s = (1..max).takeWhile { int it -> num % it == 0 } 42 | def b = s.size() == max - min + 1 43 | b 44 | } 45 | 46 | @CompileStatic 47 | @TypeChecked 48 | boolean divisible2(int num, int min, int max) { 49 | min.to(max).forall({ int it -> num % it == 0 } as F) 50 | } 51 | 52 | @TypeChecked 53 | @CompileStatic 54 | int lowest(int min, int max) { 55 | def s3 = Stream.forever(Enumerator.intEnumerator, max, max) 56 | // def s = Stream.range(1) 57 | def s2 = s3.dropWhile({ Integer it -> 58 | def b = !divisible(it, min, max) 59 | b 60 | } as F) 61 | def val = s2.head() 62 | val 63 | } 64 | 65 | @Test 66 | void test1() { 67 | assertTrue(divisible(6, 1, 3)) 68 | assertTrue(lowest(1, 3) == 6) 69 | } 70 | 71 | @Test 72 | void test2() { 73 | def val = lowest(1, 10) 74 | assertTrue(val == 2520) 75 | } 76 | 77 | // @Test 78 | // @Ignore 79 | // void testHigh() { 80 | // def v2 = lowest(1, 20) 81 | // assertTrue(v2 == 232792560) 82 | // } 83 | // 84 | // @Test 85 | // @Ignore 86 | // void myTestMid18() { 87 | // def val = lowest(1, 18) 88 | // println val 89 | // assertTrue(val == 12252240) 90 | // } 91 | // 92 | // @Test 93 | // @Ignore 94 | // void myTestMid19() { 95 | // def val = lowest(1, 19) 96 | // println val 97 | // assertTrue(val == 232792560) 98 | // } 99 | // 100 | 101 | @Test 102 | void test5() { 103 | def val = lowest(1, 6) 104 | println val 105 | assertTrue(val == 60) 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/test/IntegerOverflow.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test 2 | 3 | import org.junit.Test 4 | 5 | import static Specification.spec 6 | import static com.github.mperry.fg.test.Specification.specAssert 7 | import static fj.data.Option.some 8 | 9 | /** 10 | * Created with IntelliJ IDEA. 11 | * User: MarkPerry 12 | * Date: 30/11/13 13 | * Time: 10:07 PM 14 | * To change this template use File | Settings | File Templates. 15 | */ 16 | class IntegerOverflow { 17 | 18 | /** 19 | * It is false, that given a and b is a natural, that a + b is a natural, due to overflow 20 | */ 21 | @Test 22 | void overflow() { 23 | specAssert new Model( 24 | truth: false, 25 | pre: some { a, b -> a >= 0 && b >= 0 }, 26 | function: { Integer a, Integer b -> 27 | a + b >= 0 28 | } 29 | ) 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/test/ListFunctorLawsTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test 2 | 3 | import fj.F 4 | import fj.F3 5 | import groovy.transform.TypeChecked 6 | import org.junit.Test 7 | 8 | import static Specification.spec 9 | import static com.github.mperry.fg.test.Specification.specAssert 10 | import static fj.Function.compose 11 | import static fj.test.Arbitrary.* 12 | import static fj.test.Coarbitrary.* 13 | import static fj.test.Property.prop 14 | import static fj.test.Property.property 15 | 16 | /** 17 | * Created with IntelliJ IDEA. 18 | * User: MarkPerry 19 | * Date: 6/12/13 20 | * Time: 3:31 PM 21 | * To change this template use File | Settings | File Templates. 22 | */ 23 | 24 | /* 25 | Checks the two functor laws on List.map. These laws are: 26 | 1) The Law of Identity 27 | forall x. map identity x == x 28 | 29 | For any list, mapping the identity function (\x -> x) produces the same list. 30 | 31 | 2) The Law of Composition 32 | forall f. forall g. forall x. map (f . g) x == map f (map g x) 33 | 34 | ...where (f . g) denotes composition of f with g. That is, \c -> f(g(c)). 35 | 36 | Note that to test this second law requires the generation of arbitrary functions. 37 | */ 38 | 39 | @TypeChecked 40 | class ListFunctorLawsTest { 41 | 42 | @Test 43 | void identity() { 44 | specAssert { List list -> 45 | list == list.collect { 46 | it 47 | } 48 | } 49 | } 50 | 51 | @Test 52 | void composition2() { 53 | def p2 = property(arbF(coarbInteger, arbString), arbF(coarbLong, arbInteger), arbList(arbLong), { 54 | F f, F g, fj.data.List x -> 55 | def y = x.toJavaList() 56 | def list1 = y.map(compose(f, g)) 57 | def list2 = y.map(g).map(f) 58 | prop(list1 == list2) 59 | } as F3 60 | ) 61 | p2.checkBooleanWithNullableSummary(true) 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/test/ListTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test 2 | 3 | import groovy.transform.TypeChecked 4 | import org.junit.Test 5 | 6 | import static Specification.spec 7 | import static com.github.mperry.fg.test.Specification.specAssert 8 | 9 | //import fj.data.List 10 | 11 | /** 12 | * Created with IntelliJ IDEA. 13 | * User: MarkPerry 14 | * Date: 30/11/13 15 | * Time: 10:46 PM 16 | * To change this template use File | Settings | File Templates. 17 | */ 18 | class ListTest { 19 | 20 | @Test 21 | @TypeChecked 22 | void testConcatenationSize() { 23 | specAssert { List list1, List list2 -> 24 | (list1 + list2).size() == list1.size() + list2.size() 25 | } 26 | } 27 | 28 | /** 29 | * Test ArrayList concatenation, uses same arbitrary generator as testConcatenationSize, 30 | * but with concrete subclass 31 | */ 32 | @Test 33 | void testArrayListConcatenationSize() { 34 | specAssert { ArrayList list1, ArrayList list2 -> 35 | (list1 + list2).size() == list1.size() + list2.size() 36 | } 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/test/dbc/ExceptionFreeStack.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test.dbc 2 | 3 | /** 4 | * Created with IntelliJ IDEA. 5 | * User: PerryMa 6 | * Date: 2/07/13 7 | * Time: 9:37 AM 8 | * To change this template use File | Settings | File Templates. 9 | */ 10 | 11 | import org.gcontracts.annotations.* 12 | 13 | @Invariant({ elements != null && size() >= 0 }) 14 | class ExceptionFreeStack { 15 | 16 | List elements 17 | 18 | @Ensures({ isEmpty() }) 19 | ExceptionFreeStack() { 20 | elements = [] 21 | } 22 | 23 | @Requires({ list != null }) 24 | @Ensures({ size() == list.size() }) 25 | ExceptionFreeStack(List list) { 26 | elements = new ArrayList(list) 27 | } 28 | 29 | @Requires({ stack != null }) 30 | @Ensures({ size() == stack.size() }) 31 | ExceptionFreeStack(ExceptionFreeStack stack) { 32 | elements = new ArrayList(stack.elements) 33 | } 34 | 35 | boolean isEmpty() { 36 | elements.isEmpty() 37 | } 38 | 39 | @Requires({ !isEmpty() }) 40 | T top() { 41 | elements.last() 42 | } 43 | 44 | @Ensures({ result -> result >= 0 }) 45 | int size() { 46 | elements.size() 47 | } 48 | 49 | @Ensures({ result -> result.implies(size() > 0) }) 50 | boolean has(T item) { 51 | elements.contains(item) 52 | } 53 | 54 | // TODO: appears to be a bug in GContracts where old is a LinkedHashMap which is always empty, so the 55 | // following does not work 56 | // old -> top() == item && old.elements.size() + 1 == size() 57 | // old -> compare(old) 58 | @Ensures({ top() == item }) 59 | void push(T item) { 60 | elements.add(item) 61 | } 62 | 63 | @Requires({ !isEmpty() }) 64 | T pop() { 65 | elements.pop() 66 | } 67 | 68 | String toString() { 69 | elements.toString() 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/test/dbc/ExceptionFreeStackTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test.dbc 2 | 3 | import com.github.mperry.fg.test.DbcContractValidator 4 | import com.github.mperry.fg.test.Model 5 | import fj.F 6 | import fj.test.Arbitrary 7 | import fj.test.Gen 8 | import groovy.transform.TypeChecked 9 | import groovy.transform.TypeCheckingMode 10 | import org.junit.Test 11 | 12 | import static Model.getDEFAULT_MAP 13 | import static com.github.mperry.fg.test.Specification.spec 14 | import static fj.test.Arbitrary.arbitrary 15 | 16 | /** 17 | * Created with IntelliJ IDEA. 18 | * User: mwp 19 | * Date: 3/12/13 20 | * Time: 8:26 AM 21 | * To change this template use File | Settings | File Templates. 22 | */ 23 | @TypeChecked 24 | class ExceptionFreeStackTest { 25 | 26 | ExceptionFreeStack empty() { 27 | new ExceptionFreeStack() 28 | } 29 | 30 | Gen genStackSize() { 31 | Gen.oneOf([Gen.value(0), Gen.value(1), Gen.choose(2, 10)].toFJList()) 32 | } 33 | 34 | Gen> genStackImperative() { 35 | genStackSize().map({ Integer n -> 36 | def s = empty() 37 | def r = new Random() 38 | // println "Creating stack of size $n" 39 | for (int i = 0; i < n; i++) { 40 | def val = r.nextInt() 41 | s.push(val) 42 | } 43 | s 44 | } as F) 45 | } 46 | 47 | Gen> genEmpty() { 48 | Gen.value(empty()) 49 | } 50 | 51 | Gen> genNonEmpty() { 52 | Arbitrary.arbInteger.gen.bind({Integer i -> 53 | def g = genStackRecursive().map({ ExceptionFreeStack s -> 54 | s.push(i) 55 | s 56 | } as F) 57 | } as F) 58 | } 59 | 60 | Gen> genStackRecursive() { 61 | Gen.oneOf([genEmpty(), genNonEmpty()].toFJList()) 62 | } 63 | 64 | Arbitrary> arbStack() { 65 | arbitrary(genStackRecursive()) 66 | } 67 | 68 | @Test 69 | @TypeChecked(TypeCheckingMode.SKIP) 70 | void testPush() { 71 | [genStackRecursive(), genStackImperative()].each { g -> 72 | spec new Model( 73 | map: DEFAULT_MAP + [(ExceptionFreeStack.class): arbitrary(g)], 74 | function: { ExceptionFreeStack s, Integer i -> 75 | // println "pushing $i onto ${s.toString()}" 76 | def newStack = new ExceptionFreeStack(s) 77 | newStack.push(i) 78 | def b = newStack.top() == i 79 | b 80 | }, 81 | validator: DbcContractValidator.validateValidation() 82 | ) 83 | } 84 | } 85 | 86 | @Test 87 | @TypeChecked(TypeCheckingMode.SKIP) 88 | void testPop() { 89 | spec new Model( 90 | map: DEFAULT_MAP + [(ExceptionFreeStack.class): arbStack()], 91 | function: { ExceptionFreeStack s -> 92 | // println "pushing $i onto ${s.toString()}" 93 | def val = s.pop() 94 | true 95 | }, 96 | validator: DbcContractValidator.validateValidation() 97 | ) 98 | } 99 | 100 | } 101 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/test/dbc/TotalStack.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test.dbc 2 | 3 | import fj.data.Option 4 | 5 | import static fj.data.Option.none 6 | import static fj.data.Option.some 7 | 8 | /** 9 | * Simple total Stack (returns no exceptions) 10 | */ 11 | class TotalStack { 12 | 13 | List elements 14 | 15 | TotalStack() { 16 | elements = [] 17 | } 18 | 19 | boolean isEmpty() { 20 | elements.isEmpty() 21 | } 22 | 23 | Option top() { 24 | isEmpty() ? none() : some(elements.last()) 25 | } 26 | 27 | int size() { 28 | elements.size() 29 | } 30 | 31 | void push(T item) { 32 | elements.add(item) 33 | } 34 | 35 | Option pop() { 36 | isEmpty() ? none() : some(elements.pop()) 37 | } 38 | 39 | String toString() { 40 | elements.toString() 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /main/src/test/groovy/com/github/mperry/fg/test/dbc/TotalStackTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.test.dbc 2 | 3 | import com.github.mperry.fg.test.Model 4 | import fj.F 5 | import fj.test.Arbitrary 6 | import fj.test.Gen 7 | import groovy.transform.TypeChecked 8 | import groovy.transform.TypeCheckingMode 9 | import org.junit.Test 10 | 11 | import static com.github.mperry.fg.test.Model.getDEFAULT_MAP 12 | import static com.github.mperry.fg.test.Specification.spec 13 | import static com.github.mperry.fg.test.Specification.specAssert 14 | import static fj.test.Arbitrary.arbitrary 15 | 16 | /** 17 | * Created with IntelliJ IDEA. 18 | * User: MarkPerry 19 | * Date: 10/12/13 20 | * Time: 8:59 AM 21 | * To change this template use File | Settings | File Templates. 22 | */ 23 | class TotalStackTest { 24 | 25 | TotalStack empty() { 26 | new TotalStack() 27 | } 28 | 29 | Gen genStackSize() { 30 | Gen.oneOf([Gen.value(0), Gen.value(1), Gen.choose(2, 10)].toFJList()) 31 | } 32 | 33 | Gen> genStackLoop() { 34 | genStackSize().map({ Integer n -> 35 | def s = empty() 36 | def r = new Random() 37 | for (int i = 0; i < n; i++) { 38 | s.push(r.nextInt()) 39 | } 40 | s 41 | } as F) 42 | } 43 | 44 | Gen> genEmpty() { 45 | Gen.value(empty()) 46 | } 47 | 48 | Gen> genNonEmpty() { 49 | Arbitrary.arbInteger.gen.bind({Integer i -> 50 | genStackRecursive().map({ TotalStack s -> 51 | s.push(i) 52 | s 53 | } as F) 54 | } as F) 55 | } 56 | 57 | Gen> genStackRecursive() { 58 | Gen.oneOf([genEmpty(), genNonEmpty()].toFJList()) 59 | } 60 | 61 | Arbitrary> arbStack() { 62 | arbitrary(genStackRecursive()) 63 | } 64 | 65 | @Test 66 | // @TypeChecked(TypeCheckingMode.SKIP) 67 | void testPush() { 68 | [genStackRecursive(), genStackLoop()].each { g -> 69 | specAssert new Model( 70 | map: DEFAULT_MAP + [(TotalStack.class): arbitrary(g)], 71 | function: { TotalStack s, Integer i -> 72 | s.push(i) 73 | def val = s.top() 74 | val.map { it == i }.orSome(false) 75 | } 76 | ) 77 | } 78 | } 79 | 80 | @Test 81 | @TypeChecked(TypeCheckingMode.SKIP) 82 | void testTop() { 83 | def r = new Random() 84 | specAssert new Model( 85 | map: DEFAULT_MAP + [(TotalStack.class): arbStack()], 86 | function: { TotalStack s -> 87 | def i = r.nextInt() 88 | s.push(i) 89 | s.top().map{ it == i}.orSome(false) 90 | } 91 | ) 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /main/src/test/resources/lift.properties: -------------------------------------------------------------------------------- 1 | 2 | var1 = myvar1 3 | var2 = myvar2 4 | #var2Missing = 5 | var3 = 3 6 | var4 = 4 7 | var5 = 5 8 | var6 = six 9 | 10 | -------------------------------------------------------------------------------- /sandbox/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | ext { 3 | 4 | } 5 | 6 | dependencies { 7 | 8 | compile project(":core") 9 | 10 | } 11 | -------------------------------------------------------------------------------- /sandbox/src/main/groovy/com/github/mperry/A.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry 2 | 3 | import groovy.transform.Canonical 4 | import groovy.transform.TypeChecked 5 | 6 | /** 7 | * Created by mperry on 10/09/2014. 8 | */ 9 | @TypeChecked 10 | @Canonical 11 | class A { 12 | 13 | Object asType(Class clazz) { 14 | if (clazz == B) { 15 | new B() 16 | } 17 | 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /sandbox/src/main/groovy/com/github/mperry/B.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry 2 | 3 | import groovy.transform.Canonical 4 | import groovy.transform.TypeChecked 5 | 6 | /** 7 | * Created by mperry on 10/09/2014. 8 | */ 9 | @TypeChecked 10 | //@Canonical 11 | class B { 12 | 13 | B(){} 14 | B(A a) {} 15 | 16 | Object asType(Class clazz) { 17 | if (clazz == A) { 18 | new A() 19 | } 20 | 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /sandbox/src/test/groovy/com/github/mperry/TestCoercion.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry 2 | 3 | import groovy.transform.TypeChecked 4 | import org.junit.Test 5 | 6 | /** 7 | * Created by mperry on 10/09/2014. 8 | */ 9 | @TypeChecked 10 | class TestCoercion { 11 | 12 | @Test 13 | void test1() { 14 | 15 | callb(new B()) 16 | 17 | def a1 = new A() 18 | B b1 = new B() 19 | B b2 = [a1] 20 | B b3 = new B(a1) 21 | 22 | callb(b1) 23 | 24 | callb(a1 as B) 25 | callb(b2) 26 | callb([a1] as B) 27 | 28 | callb(b3) 29 | 30 | 31 | } 32 | 33 | 34 | void callb(B b) { 35 | 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | 2 | rootProject.name = "functionalgroovy" 3 | 4 | include 'core', "typeclass", 'main', 'demo', "test-extensions", "java8", "kind", "sandbox", "base" 5 | 6 | -------------------------------------------------------------------------------- /test-extensions/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | ext { 3 | 4 | } 5 | 6 | dependencies { 7 | compile project(":main") 8 | } 9 | -------------------------------------------------------------------------------- /typeclass/build.gradle: -------------------------------------------------------------------------------- 1 | 2 | ext { 3 | 4 | } 5 | 6 | uploadArchives.enabled = true 7 | 8 | dependencies { 9 | compile gradleGroovy 10 | compile gradleFj 11 | compile project(":core") 12 | compile project(":base") 13 | testCompile gradleJunit 14 | } 15 | 16 | 17 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/Applicative.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass 2 | 3 | //import com.github.mperry.fg.Functions 4 | import com.github.mperry.fg.ListOps 5 | import fj.F 6 | import fj.F1Functions 7 | import fj.F2 8 | import fj.F3 9 | import fj.F3Functions 10 | import fj.Function 11 | import groovy.transform.TypeChecked 12 | 13 | /** 14 | * Created by MarkPerry on 9/04/2014. 15 | * @see http://www.haskell.org/haskellwiki/Typeclassopedia 16 | * @see http://hackage.haskell.org/package/base-4.2.0.0/docs/Control-Applicative.html 17 | * 18 | * Laws: 19 | * identity: pure id <*> v = v 20 | * composition: pure (.) <*> u <*> v <*> w = u <*> (v <*> w) 21 | * homomorphism: pure f <*> pure x = pure (f x) 22 | * interchange: u <*> pure y = pure ($ y) <*> u 23 | * ignore left value: u *> v = pure (const id) <*> u <*> v 24 | * ignore right value: u <* v = pure const <*> u <*> v 25 | */ 26 | @TypeChecked 27 | abstract class Applicative implements Functor { 28 | 29 | /** 30 | * lift value into Applicative 31 | * pure :: a -> f a 32 | */ 33 | abstract App pure(A a) 34 | 35 | /** 36 | * Sequence computations and combine their results 37 | * (<*>) :: f (a -> b) -> f a -> f b 38 | */ 39 | abstract App apply(App> t1, App t2) 40 | 41 | /** 42 | * (<*) :: f a -> f b -> f a 43 | */ 44 | def App left(App a1, App a2) { 45 | a1 46 | } 47 | 48 | /** 49 | * (*>) :: f a -> f b -> f b 50 | */ 51 | def App right(App a1, App a2) { 52 | a2 53 | } 54 | 55 | /** 56 | * liftA :: Applicative f => (a -> b) -> f a -> f b 57 | */ 58 | def App liftA(F f, App a1) { 59 | apply(pure(f), a1) 60 | } 61 | 62 | /** 63 | * liftA2 :: Applicative f => (a -> b -> c) -> f a -> f b -> f c 64 | */ 65 | def App liftA2(F2 f, App apa, App apb) { 66 | apply(map(apa, Function.curry(f)), apb) 67 | } 68 | 69 | def F3, App, App, App> liftA2_() { 70 | { F2 f2, App a, App b -> 71 | liftA2(f2, a, b) 72 | } as F3 73 | } 74 | 75 | /** 76 | * liftA3 :: Applicative f => (a -> b -> c -> d) -> f a -> f b -> f c -> f d 77 | */ 78 | def App liftA3(F3 f, App apa, App apb, App apc) { 79 | apply(apply(map(apa, Function.curry(f)), apb), apc) 80 | } 81 | 82 | def App> sequenceA(List> list) { 83 | def cons = { 84 | A a, List listAs -> ListOps.plus(a, listAs) 85 | } as F2 86 | // def cons_ = { 87 | // A a -> 88 | // { List listAs -> ListOps.plus(a, listAs) } as F 89 | // } as F 90 | def f2 = F3Functions.f(liftA2_(), cons) 91 | list.foldRight(pure([]), f2) 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/Comonad.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass 2 | 3 | import com.github.mperry.fg.YCombinator 4 | import fj.F 5 | import fj.F1Functions 6 | import fj.F2 7 | import fj.F2Functions 8 | import groovy.transform.TypeChecked 9 | import groovy.transform.TypeCheckingMode 10 | 11 | import static fj.F2Functions.* 12 | 13 | /** 14 | * Created by MarkPerry on 28/12/2014. 15 | */ 16 | @TypeChecked 17 | abstract class Comonad implements Functor { 18 | 19 | def CM map(CM fa, F f) { 20 | liftW(fa, f) 21 | } 22 | 23 | /** 24 | * extract :: w a -> a 25 | */ 26 | abstract A extract(CM c); 27 | 28 | /** 29 | * duplicate :: w a -> w (w a) 30 | */ 31 | def CM> duplicate(CM c) { 32 | def id = { A a -> a } as F 33 | flip(extend()).f(id, c) 34 | } 35 | 36 | /** 37 | * extend :: (w a -> b) -> w a -> w b 38 | */ 39 | def CM extend (CM c, F, B> f) { 40 | this.map(duplicate(c), f) 41 | } 42 | 43 | def F2, F, B>, CM> extend() { 44 | { CM cm, F, B> f -> extend(cm, f) } as F2 45 | } 46 | 47 | /** 48 | * liftW :: Comonad w => (a -> b) -> w a -> w b 49 | */ 50 | def CM liftW(CM cm, F f) { 51 | extend(cm, { CM cma -> f.f(this.extract(cma)) } as F) 52 | } 53 | 54 | /** 55 | * wfix :: Comonad w => w (w a -> a) -> a 56 | * wfix w = extract w (extend wfix w) 57 | */ 58 | def A wfix(CM, A>> c) { 59 | extract(extend(c, wfix())) 60 | } 61 | 62 | def F, A>>, A> wfix() { 63 | { CM, A>> cm -> wfix(cm) } as F 64 | } 65 | 66 | // @TypeChecked(TypeCheckingMode.SKIP) 67 | // def CM cfix(F, A> f) { 68 | //// def id = { CM cm -> cm } as F 69 | //// def id2 = { Z z -> Z } 70 | // def result = F2Functions.f(flip(extend()), f); 71 | // return YCombinator.Y({ def g -> { def a -> result.f(a) } }) 72 | //// this.map(result, id) 73 | //// def result = flip(extend()).f(f, id) 74 | // // TODO: call fix(result) 75 | // 76 | // // hard to do: implement at some future point 77 | //// throw new UnsupportedOperationException("Yet to be implemented") 78 | // } 79 | 80 | 81 | /** 82 | * (=>=) :: Comonad w => (w a -> b) -> (w b -> c) -> w a -> c 83 | * Left-to-right Cokleisli composition 84 | */ 85 | def C rightShift(F,B> f, F, C> g, CM cm) { 86 | F1Functions.o(g, F2Functions.f(flip(extend()), f)) 87 | } 88 | 89 | /** 90 | * (=<=) :: Comonad w => (w b -> c) -> (w a -> b) -> w a -> c 91 | * Right-to-left Cokleisli composition 92 | */ 93 | def C leftShift(F, C> f, F, B> g, CM cm) { 94 | F1Functions.o(f, F2Functions.f(flip(extend()), g)) 95 | } 96 | 97 | 98 | 99 | 100 | 101 | } 102 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/Functor.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass 2 | 3 | import fj.F 4 | import groovy.transform.TypeChecked 5 | import groovy.transform.TypeCheckingMode 6 | 7 | /** 8 | * Created by MarkPerry on 9/04/2014. 9 | * @see http://hackage.haskell.org/package/base-4.7.0.0/docs/Data-Functor.html 10 | * @see http://www.haskell.org/haskellwiki/Typeclassopedia 11 | * 12 | * Laws: 13 | * fmap id = id 14 | * fmap (g . h) = (fmap g) . (fmap h) 15 | */ 16 | @TypeChecked 17 | interface Functor { 18 | /** 19 | * map :: (a -> b) -> f a -> f b 20 | * order of first and second parameters reversed here to fit Groovy style 21 | */ 22 | abstract T map(T fa, F f) 23 | } 24 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/MonadPlus.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 12/04/2014. 7 | * TODO: Implement MonadPlus methods 8 | * @see http://hackage.haskell.org/package/base-4.7.0.0/docs/Control-Monad.html 9 | */ 10 | @TypeChecked 11 | class MonadPlus { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/MonadTrans.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass 2 | 3 | import fj.F 4 | import groovy.transform.TypeChecked 5 | 6 | /** 7 | * Created by MarkPerry on 27/12/2014. 8 | * 9 | * class MonadTrans t where 10 | * lift :: Monad m => m a -> t m a 11 | */ 12 | @TypeChecked 13 | abstract class MonadTrans { 14 | 15 | abstract T unit(M m, Monad monad) 16 | 17 | abstract T flatMap(T t, Monad m, F> f) 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/Monoid.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 9/04/2014. 7 | */ 8 | @TypeChecked 9 | interface Monoid { 10 | 11 | /** 12 | * mempty :: a 13 | */ 14 | A mempty() 15 | 16 | /** 17 | * mappend :: a -> a -> a 18 | */ 19 | A mappend(A a1, A a2) 20 | 21 | } 22 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/Semigroup.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass 2 | 3 | import groovy.transform.TypeChecked 4 | 5 | /** 6 | * Created by MarkPerry on 9/04/2014. 7 | */ 8 | @TypeChecked 9 | interface Semigroup { 10 | 11 | /** 12 | * (<>) :: a -> a -> a 13 | */ 14 | A append(A a1, A a2) 15 | 16 | } 17 | 18 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/concrete/IOMonad.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass.concrete 2 | 3 | import com.github.mperry.fg.typeclass.Monad 4 | import fj.F 5 | import fj.data.IO 6 | import fj.data.IOFunctions 7 | import groovy.transform.TypeChecked 8 | 9 | /** 10 | * Created by MarkPerry on 15/07/2014. 11 | */ 12 | @TypeChecked 13 | class IOMonad extends Monad { 14 | 15 | @Override 16 | def IO unit(A a) { 17 | { -> a } as IO 18 | } 19 | 20 | @Override 21 | def IO flatMap(IO io, F> f) { 22 | // IOFunctions.bind(io, f) 23 | { -> f.f(io.run()).run() } as IO 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/concrete/ListApplicative.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass.concrete 2 | 3 | import com.github.mperry.fg.typeclass.Applicative 4 | import fj.F 5 | import fj.P2 6 | import groovy.transform.TypeChecked 7 | 8 | /** 9 | * Created by MarkPerry on 10/04/2014. 10 | */ 11 | @TypeChecked 12 | class ListApplicative extends Applicative { 13 | 14 | @Override 15 | def List pure(A a) { 16 | [a] 17 | } 18 | 19 | @Override 20 | def List apply(List> fs, List list) { 21 | fs.flatMap { F f -> 22 | list.map { A a -> 23 | f.f(a) 24 | } 25 | } 26 | } 27 | 28 | @Override 29 | def List map(List list, F f) { 30 | list.map(f) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/concrete/ListFunctor.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass.concrete 2 | 3 | import com.github.mperry.fg.typeclass.Functor 4 | import fj.F 5 | import groovy.transform.TypeChecked 6 | 7 | /** 8 | * Created by MarkPerry on 10/04/2014. 9 | */ 10 | @TypeChecked 11 | class ListFunctor implements Functor { 12 | 13 | @Override 14 | def List map(List list, F f) { 15 | list.map(f) 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/concrete/ListMonad.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass.concrete 2 | 3 | import com.github.mperry.fg.typeclass.Monad 4 | import fj.F 5 | import fj.F2 6 | import groovy.transform.TypeChecked 7 | 8 | /** 9 | * Created by MarkPerry on 10/04/2014. 10 | */ 11 | @TypeChecked 12 | class ListMonad extends Monad { 13 | 14 | @Override 15 | def List flatMap(List ma, F> f) { 16 | ma.flatMap(f) 17 | } 18 | 19 | 20 | @Override 21 | def List unit(A a) { 22 | [a] 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/concrete/OptionApplicative.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass.concrete 2 | 3 | import com.github.mperry.fg.typeclass.Applicative 4 | import fj.F 5 | import fj.data.Option 6 | import groovy.transform.TypeChecked 7 | 8 | /** 9 | * Created by MarkPerry on 10/04/2014. 10 | */ 11 | @TypeChecked 12 | class OptionApplicative extends Applicative Option pure(A a) { 16 | Option.some(a) 17 | } 18 | 19 | @Override 20 | def Option apply(Option> optF, Option o) { 21 | o.flatMap { A a -> 22 | optF.map { F f -> 23 | f.f(a) 24 | } 25 | } 26 | } 27 | 28 | @Override 29 | def Option map(Option fa, F f) { 30 | fa.map(f) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/concrete/OptionFunctor.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass.concrete 2 | 3 | import com.github.mperry.fg.typeclass.Functor 4 | import fj.F 5 | import fj.data.Option 6 | import groovy.transform.TypeChecked 7 | 8 | /** 9 | * Created by MarkPerry on 10/04/2014. 10 | */ 11 | @TypeChecked 12 | class OptionFunctor implements Functor fa, F f) { 16 | fa.map(f) 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/concrete/OptionMonad.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass.concrete 2 | 3 | import com.github.mperry.fg.typeclass.Monad 4 | import fj.F 5 | import fj.data.Option 6 | import groovy.transform.TypeChecked 7 | 8 | /** 9 | * Created by MarkPerry on 10/04/2014. 10 | */ 11 | @TypeChecked 12 | class OptionMonad extends Monad ma, F> f) { 16 | ma.flatMap(f) 17 | } 18 | 19 | @Override 20 | def Option unit(A a) { 21 | Option.some(a) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/concrete/OptionMonadTrans.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass.concrete 2 | 3 | import com.github.mperry.fg.typeclass.Monad 4 | import com.github.mperry.fg.typeclass.MonadTrans 5 | import fj.F 6 | import fj.data.Option 7 | import groovy.transform.TypeChecked 8 | 9 | /** 10 | * Created by MarkPerry on 27/12/2014. 11 | */ 12 | @TypeChecked 13 | class OptionMonadTrans extends MonadTrans { 14 | 15 | @Override 16 | def OptionT unit(M m, Monad monad) { 17 | OptionT.point(monad.map(m, { A a -> Option.some(a) } as F), monad) 18 | } 19 | 20 | @Override 21 | def OptionT flatMap(OptionT t, Monad m, F> f) { 22 | unit(m.flatMap(t.value, { Option o -> 23 | if (o.isNone()) { 24 | m.unit(o.none()) 25 | } else { 26 | f.f(o.some()).value 27 | } 28 | } as F), m) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/concrete/OptionT.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass.concrete 2 | 3 | import com.github.mperry.fg.typeclass.Functor 4 | import com.github.mperry.fg.typeclass.Monad 5 | import fj.F 6 | import fj.data.Option 7 | import groovy.transform.TypeChecked 8 | 9 | /** 10 | * Created by MarkPerry on 27/12/2014. 11 | */ 12 | @TypeChecked 13 | class OptionT { 14 | 15 | M> value 16 | Monad monad 17 | 18 | OptionT(M> v, Monad m) { 19 | value = v 20 | monad = m 21 | } 22 | 23 | static OptionT point(M> m, Monad monad) { 24 | new OptionT(m, monad) 25 | } 26 | 27 | def OptionT map(F f) { 28 | point(monad.map(value, { Option o -> o.map(f)} as F), monad) 29 | } 30 | 31 | def OptionT flatMap(F> f) { 32 | point(monad.flatMap(value, { Option o -> 33 | if (o.isNone()) { 34 | monad.unit(o.none()) 35 | } else { 36 | f.f(o.some()).value 37 | } 38 | } as F), monad) 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/concrete/SafeIOMonad.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass.concrete 2 | 3 | import com.github.mperry.fg.typeclass.Monad 4 | import fj.F 5 | import fj.data.IO 6 | import fj.data.IOFunctions 7 | import fj.data.SafeIO 8 | import fj.data.Validation 9 | import groovy.transform.TypeChecked 10 | 11 | /** 12 | * Created by MarkPerry on 7/09/2014. 13 | */ 14 | @TypeChecked 15 | class SafeIOMonad extends Monad { 16 | 17 | @Override 18 | def SafeIO unit(A a) { 19 | { -> a } as SafeIO 20 | } 21 | 22 | @Override 23 | def SafeIO flatMap(SafeIO io, F> f) { 24 | { -> IOFunctions.flatMap(io, { A a -> { -> f.f(a).run() } as IO }) } as SafeIO 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/concrete/SetMonad.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass.concrete 2 | 3 | import com.github.mperry.fg.typeclass.Monad 4 | import fj.F 5 | import groovy.transform.TypeChecked 6 | 7 | /** 8 | * Created by MarkPerry on 10/04/2014. 9 | */ 10 | @TypeChecked 11 | class SetMonad extends Monad { 12 | 13 | static Set defaultSet() { 14 | new HashSet() 15 | } 16 | 17 | @Override 18 | def Set flatMap(Set ma, F> f) { 19 | def result = this.defaultSet() 20 | for (A a: ma) { 21 | result.addAll(f.f(a)) 22 | } 23 | result 24 | } 25 | 26 | @Override 27 | def Set unit(A b) { 28 | def result = this.defaultSet() 29 | result.add(b) 30 | result 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/concrete/StateInt.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass.concrete 2 | 3 | import fj.F 4 | import fj.P2 5 | import fj.data.State 6 | import groovy.transform.TypeChecked 7 | 8 | /** 9 | * Created by MarkPerry on 15/10/2014. 10 | */ 11 | @TypeChecked 12 | class StateInt extends State { 13 | 14 | StateInt(F> f) { 15 | run = f 16 | } 17 | 18 | def StateInt flatMap(F> f) { 19 | new StateInt({ Integer s -> 20 | def p = run.f(s) 21 | def a = p._2() 22 | def s2 = p._1() 23 | 24 | def sib = f.f(a) 25 | sib.run.f(s2) 26 | } as F) 27 | } 28 | 29 | static StateInt unit(F> f) { 30 | new StateInt(f) 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /typeclass/src/main/groovy/com/github/mperry/fg/typeclass/concrete/StateIntMonad.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass.concrete 2 | 3 | import com.github.mperry.fg.typeclass.Monad 4 | import fj.F 5 | import fj.P 6 | import fj.data.State 7 | import groovy.transform.TypeChecked 8 | 9 | /** 10 | * Created by MarkPerry on 15/10/2014. 11 | */ 12 | @TypeChecked 13 | class StateIntMonad extends Monad { 14 | 15 | @Override 16 | def StateInt flatMap(StateInt ma, F> f) { 17 | ma.flatMap(f) 18 | } 19 | 20 | @Override 21 | def StateInt unit(A a) { 22 | StateInt.unit({ Integer i -> P.p(i, a)}) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /typeclass/src/test/groovy/com/github/mperry/fg/typeclass/FunctorTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass 2 | 3 | import com.github.mperry.fg.typeclass.concrete.ListFunctor 4 | import com.github.mperry.fg.typeclass.concrete.OptionFunctor 5 | import fj.F 6 | import fj.F1Functions 7 | import fj.F2Functions 8 | import fj.function.Integers 9 | import groovy.transform.TypeChecked 10 | import org.junit.Test 11 | 12 | import static fj.F2Functions.curry 13 | import static fj.data.Option.some 14 | 15 | /** 16 | * Created by MarkPerry on 10/04/2014. 17 | */ 18 | @TypeChecked 19 | class FunctorTest { 20 | 21 | @Test 22 | void test1() { 23 | def optionFunctor = new OptionFunctor() 24 | def listFunctor = new ListFunctor() 25 | def m = Integers.multiply 26 | def list1 = listFunctor.map([1, 2, 3], curry({ Integer a, Integer b -> a * b })) 27 | def list2 = listFunctor.map(list1, { F f -> f.f(3) }) 28 | println list1 29 | println list2 30 | 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /typeclass/src/test/groovy/com/github/mperry/fg/typeclass/IOMonadTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass 2 | 3 | import com.github.mperry.fg.typeclass.concrete.IOMonad 4 | import fj.data.IO 5 | import groovy.transform.TypeChecked 6 | import org.junit.Test 7 | 8 | /** 9 | * Created by MarkPerry on 7/09/2014. 10 | */ 11 | @TypeChecked 12 | class IOMonadTest { 13 | 14 | static IOMonad monad = new IOMonad() 15 | 16 | static IO> listFiles(File f) { 17 | { -> 18 | def files = new ArrayList() 19 | files.addAll(f.listFiles()) 20 | files 21 | } as IO> 22 | } 23 | 24 | static IO> listFiles() { 25 | listFiles(new File(".")) 26 | } 27 | 28 | static IO size(File f) { 29 | { -> f.length() } as IO 30 | } 31 | 32 | static IO info(File f) { 33 | { -> "${f.name}:${f.length()}" } as IO 34 | } 35 | 36 | @Test 37 | void sequence() { 38 | def io = monad.flatMap(listFiles(), { List list -> 39 | monad.sequence(list.map{ File f -> info(f) }) as IO> 40 | }) 41 | println(io.run().join("\n")) 42 | } 43 | 44 | @Test 45 | void traverse() { 46 | def io = monad.flatMap(listFiles(), { List list -> 47 | monad.traverse(list, { File f -> info(f) }) as IO> 48 | }) 49 | println(io.run().join("\n")) 50 | } 51 | 52 | } 53 | -------------------------------------------------------------------------------- /typeclass/src/test/groovy/com/github/mperry/fg/typeclass/ListApplicativeTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass 2 | 3 | import com.github.mperry.fg.typeclass.concrete.ListApplicative 4 | import com.github.mperry.fg.typeclass.concrete.ListFunctor 5 | import fj.F 6 | import fj.F2 7 | import fj.F2Functions 8 | import groovy.transform.TypeChecked 9 | import org.junit.Test 10 | 11 | import static org.junit.Assert.assertTrue 12 | 13 | /** 14 | * Created by mperry on 2/07/2014. 15 | */ 16 | @TypeChecked 17 | class ListApplicativeTest { 18 | 19 | @Test 20 | void test1() { 21 | def listFunctor = new ListFunctor() 22 | def app = new ListApplicative() 23 | 24 | def list1 = (1..3).toList().map { { Integer a -> 4 + a } as F } 25 | def list2 = app.apply(list1, [1, 2, 3]) 26 | def list3 = listFunctor.map([1, 2, 3], F2Functions.curry({ Integer a, Integer b -> a * b } as F2)) 27 | def list4 = app.apply(list3, [1, 2, 3]) 28 | 29 | println list1 30 | println list2 31 | println list3 32 | println list4 33 | } 34 | 35 | @Test 36 | void testSeq() { 37 | def app = new ListApplicative() 38 | def list1 = app.sequenceA([[1, 2, 3], [4, 5, 6]]) 39 | assertTrue(list1 == [[1,4],[1,5],[1,6],[2,4],[2,5],[2,6],[3,4],[3,5],[3,6]]) 40 | println list1 41 | 42 | def list2 = app.sequenceA([[1,2,3],[4,5,6],[3,4,4],[]]) 43 | println list2 44 | println([[1, 2, 3], [4, 5, 6]].combinations()) 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /typeclass/src/test/groovy/com/github/mperry/fg/typeclass/ListFunctorTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass 2 | 3 | import com.github.mperry.fg.typeclass.concrete.ListFunctor 4 | import groovy.transform.TypeChecked 5 | import org.junit.Test 6 | 7 | import static org.junit.Assert.assertTrue 8 | 9 | /** 10 | * Created by MarkPerry on 10/04/2014. 11 | */ 12 | @TypeChecked 13 | class ListFunctorTest { 14 | 15 | @Test 16 | void test1() { 17 | def f = new ListFunctor() 18 | def param = [1, 2, 3] 19 | def c = { Integer i -> i * 2 } 20 | def actual = f.map(param, c) 21 | assertTrue(actual == param.collect(c)) 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /typeclass/src/test/groovy/com/github/mperry/fg/typeclass/MonadTransTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass 2 | 3 | import com.github.mperry.fg.typeclass.concrete.StateInt 4 | import groovy.transform.TypeChecked 5 | import org.junit.Test 6 | 7 | /** 8 | * Created by MarkPerry on 27/12/2014. 9 | */ 10 | @TypeChecked 11 | class MonadTransTest { 12 | 13 | @Test 14 | void test1() { 15 | 16 | 17 | } 18 | 19 | 20 | } 21 | -------------------------------------------------------------------------------- /typeclass/src/test/groovy/com/github/mperry/fg/typeclass/OptionApplicativeTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass 2 | 3 | import com.github.mperry.fg.typeclass.concrete.OptionApplicative 4 | import fj.F 5 | import fj.F3 6 | import fj.F3Functions 7 | import fj.Function 8 | import groovy.transform.TypeChecked 9 | import org.junit.Assert 10 | import org.junit.Test 11 | 12 | import static fj.data.Option.none 13 | import static fj.data.Option.some 14 | import static org.junit.Assert.assertTrue 15 | 16 | /** 17 | * Created by mperry on 2/07/2014. 18 | */ 19 | @TypeChecked 20 | class OptionApplicativeTest { 21 | 22 | @Test 23 | void test1() { 24 | def app = new OptionApplicative() 25 | F f = { Integer a -> 3 + a } as F 26 | def o1 = app.apply(some(f), some(10)) 27 | def o2 = app.apply(some({ Integer a -> 3 + a } as F), some(10)) 28 | def o3 = app.apply(some(f), none()) 29 | 30 | // use the discriminate for quadratic equations: b^2 - 4ac 31 | F3 f3 = { Integer a, Integer b, Integer c -> b * b - 4 * a * c } as F3 32 | def o4 = app.apply(app.apply(app.apply(app.pure(Function.curry(f3)), some(4)), some(5)), some(3)) 33 | // note, with infix methods we could have written: 34 | // app.pure(Function.curry(f3)) app.apply some(4) app.apply some(5) app.apply some(3) 35 | 36 | def o5 = app.liftA3(f3, some(4), some(5), some(3)) 37 | 38 | println o1 39 | println o2 40 | println o3 41 | println o4 42 | println o5 43 | 44 | 45 | } 46 | 47 | @Test 48 | void testSeq() { 49 | def app = new OptionApplicative() 50 | def s1 = app.sequenceA([some(3), none(), some(1)]) 51 | assertTrue(s1 == none()) 52 | def s2 = app.sequenceA([some(3), some(2), some(1)]) 53 | assertTrue(s2 == some([3, 2, 1])) 54 | // println s1 55 | // println s2 56 | 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /typeclass/src/test/groovy/com/github/mperry/fg/typeclass/OptionMonadTest.groovy: -------------------------------------------------------------------------------- 1 | package com.github.mperry.fg.typeclass 2 | 3 | import com.github.mperry.fg.typeclass.concrete.OptionMonad 4 | import fj.Equal 5 | import fj.F 6 | import fj.data.Option 7 | import groovy.transform.TypeChecked 8 | import org.junit.Test 9 | 10 | import static fj.data.Option.none 11 | import static fj.data.Option.some 12 | 13 | /** 14 | * Created by MarkPerry on 7/09/2014. 15 | */ 16 | @TypeChecked 17 | class OptionMonadTest { 18 | 19 | OptionMonad monad() { 20 | new OptionMonad() 21 | } 22 | 23 | @Test 24 | void sequence() { 25 | assert(monad().sequence([some(3), some(2), some(5)]) == some([3, 2, 5])) 26 | assert(monad().sequence([some(3), none(), some(5)]) == none()) 27 | } 28 | 29 | @Test 30 | void traverse() { 31 | def even = { Integer i -> i % 2 == 0 ? some(i) : none()} as F 32 | assert(monad().traverse([2, 4, 6], even) == some([2, 4, 6])) 33 | assert(monad().traverse([2, 3, 6], even) == none()) 34 | } 35 | 36 | 37 | } 38 | --------------------------------------------------------------------------------