├── settings.gradle ├── fnz-site ├── src │ ├── site │ │ ├── application.properties │ │ ├── banner.jpg │ │ ├── theme │ │ │ ├── images │ │ │ │ ├── bg.jpg │ │ │ │ ├── bg_old.jpg │ │ │ │ ├── callout.jpg │ │ │ │ ├── portfolio-1.jpg │ │ │ │ ├── portfolio-2.jpg │ │ │ │ ├── portfolio-3.jpg │ │ │ │ └── portfolio-4.jpg │ │ │ ├── fonts │ │ │ │ ├── glyphicons-halflings-regular.eot │ │ │ │ ├── glyphicons-halflings-regular.ttf │ │ │ │ └── glyphicons-halflings-regular.woff │ │ │ ├── font-awesome-4.1.0 │ │ │ │ ├── fonts │ │ │ │ │ ├── FontAwesome.otf │ │ │ │ │ ├── fontawesome-webfont.eot │ │ │ │ │ ├── fontawesome-webfont.ttf │ │ │ │ │ └── fontawesome-webfont.woff │ │ │ │ ├── less │ │ │ │ │ ├── fixed-width.less │ │ │ │ │ ├── core.less │ │ │ │ │ ├── bordered-pulled.less │ │ │ │ │ ├── rotated-flipped.less │ │ │ │ │ ├── larger.less │ │ │ │ │ ├── list.less │ │ │ │ │ ├── font-awesome.less │ │ │ │ │ ├── stacked.less │ │ │ │ │ ├── path.less │ │ │ │ │ ├── mixins.less │ │ │ │ │ └── spinning.less │ │ │ │ └── scss │ │ │ │ │ ├── _fixed-width.scss │ │ │ │ │ ├── _core.scss │ │ │ │ │ ├── _bordered-pulled.scss │ │ │ │ │ ├── _larger.scss │ │ │ │ │ ├── _rotated-flipped.scss │ │ │ │ │ ├── _list.scss │ │ │ │ │ ├── font-awesome.scss │ │ │ │ │ ├── _stacked.scss │ │ │ │ │ ├── _path.scss │ │ │ │ │ ├── _mixins.scss │ │ │ │ │ └── _spinning.scss │ │ │ ├── includes │ │ │ │ ├── callout.html │ │ │ │ ├── gradle.adoc │ │ │ │ ├── grab.adoc │ │ │ │ ├── about.html │ │ │ │ ├── basics.html │ │ │ │ ├── calltoaction.html │ │ │ │ ├── header.html │ │ │ │ ├── navigation.html │ │ │ │ ├── checkdocumentation.html │ │ │ │ ├── try.adoc │ │ │ │ ├── head.html │ │ │ │ ├── javascripts.html │ │ │ │ ├── configuration.html │ │ │ │ ├── basics-samples.html │ │ │ │ └── maybe.adoc │ │ │ ├── src │ │ │ │ └── com │ │ │ │ │ └── sysgears │ │ │ │ │ └── theme │ │ │ │ │ ├── Launcher.java │ │ │ │ │ ├── taglib │ │ │ │ │ └── ThemeTagLib.groovy │ │ │ │ │ ├── ResourceMapper.groovy │ │ │ │ │ └── deploy │ │ │ │ │ └── GHPagesDeployer.groovy │ │ │ ├── layouts │ │ │ │ └── default.html │ │ │ └── stylesheets │ │ │ │ └── stylish-portfolio.css │ │ ├── .cache │ │ │ └── markup │ │ │ │ └── asciidoctor.0_1_4 │ │ │ │ ├── 227caa386e2b66a508e4493e7e0c7c6e.html │ │ │ │ ├── 1ea05eebc5bf384fe3648ecba981dfe8.html │ │ │ │ ├── 3ba0ba7edd892221e3a9404de11927d7.html │ │ │ │ ├── 77fb01029d18c2753d1f73ff6afb821c.html │ │ │ │ ├── a0324a8707242c86cc5dfe0186ee19b4.html │ │ │ │ ├── e5673777036cb049a2dd306bf2b5a0c6.html │ │ │ │ ├── ab6dba8f85b9c4699537fb0a10636e47.html │ │ │ │ └── 6031286f4f22df21a61df018a5312a7f.html │ │ ├── SiteConfig.groovy │ │ ├── README.md │ │ └── content │ │ │ └── index.html │ ├── docs │ │ ├── index.ad │ │ ├── intro.ad │ │ ├── type.ad │ │ └── control.ad │ └── test │ │ └── groovy │ │ ├── TryExampleSpec.groovy │ │ └── MaybeExampleSpec.groovy └── build.gradle ├── .travis.yml ├── .gitignore ├── fnz-core ├── src │ ├── main │ │ ├── resources │ │ │ └── META-INF │ │ │ │ └── services │ │ │ │ ├── org.codehaus.groovy.transform.ASTTransformation │ │ │ │ └── org.codehaus.groovy.runtime.ExtensionModule │ │ ├── java │ │ │ └── fnz │ │ │ │ ├── data │ │ │ │ ├── Function.java │ │ │ │ ├── TypeAwareFunction.java │ │ │ │ ├── Functor.java │ │ │ │ ├── TypeIterable.java │ │ │ │ ├── Type.java │ │ │ │ ├── Applicative.java │ │ │ │ ├── Truth.java │ │ │ │ ├── MonadType.java │ │ │ │ ├── Or.java │ │ │ │ ├── Monad.java │ │ │ │ ├── Either.java │ │ │ │ ├── Maybe.java │ │ │ │ ├── ListMonad.java │ │ │ │ └── Try.java │ │ │ │ └── Fnz.java │ │ └── groovy │ │ │ └── fnz │ │ │ ├── ast │ │ │ ├── FnzAst.groovy │ │ │ ├── AstExpressionTransformerAware.groovy │ │ │ ├── flow │ │ │ │ ├── DoReturnAstTransformer.groovy │ │ │ │ ├── UnlessAstTransformer.groovy │ │ │ │ ├── LetAstTransformer.groovy │ │ │ │ └── LetmAstTransformer.groovy │ │ │ ├── MethodCallExpressionTransformer.groovy │ │ │ ├── AstUtils.groovy │ │ │ ├── DefaultClassCodeExpressionTransformer.groovy │ │ │ ├── FnzExtensionModule.groovy │ │ │ └── type │ │ │ │ └── FuncAstTransformer.groovy │ │ │ └── flow │ │ │ └── Where.groovy │ ├── test │ │ └── groovy │ │ │ └── fnz │ │ │ ├── ast │ │ │ ├── flow │ │ │ │ ├── UnlessSpecExample.groovy │ │ │ │ ├── LetSpecExample.groovy │ │ │ │ ├── UnlessSpec.groovy │ │ │ │ ├── LetSpec.groovy │ │ │ │ ├── LetmSpecExample.groovy │ │ │ │ ├── LetmSpec.groovy │ │ │ │ └── DoSpec.groovy │ │ │ └── AstUtilsSpec.groovy │ │ │ ├── data │ │ │ ├── FunctionSpec.groovy │ │ │ ├── FunctorSpec.groovy │ │ │ └── EitherSpec.groovy │ │ │ ├── test │ │ │ └── AstBaseSpec.groovy │ │ │ ├── FnzSpec.groovy │ │ │ └── flow │ │ │ └── WhereSpec.groovy │ └── codenarc │ │ └── rules.groovy └── build.gradle ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── README.md ├── gradlew.bat └── gradlew /settings.gradle: -------------------------------------------------------------------------------- 1 | include 'fnz-core' 2 | include 'fnz-site' 3 | -------------------------------------------------------------------------------- /fnz-site/src/site/application.properties: -------------------------------------------------------------------------------- 1 | grain.version = 0.6.4 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: groovy 2 | jdk: 3 | - oraclejdk7 4 | script: 5 | - ./gradlew check 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build/ 2 | .gradle/ 3 | *.class 4 | .idea/ 5 | **/*.iml 6 | local.properties 7 | target/ 8 | -------------------------------------------------------------------------------- /fnz-site/src/site/banner.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mariogarcia/fnz/HEAD/fnz-site/src/site/banner.jpg -------------------------------------------------------------------------------- /fnz-core/src/main/resources/META-INF/services/org.codehaus.groovy.transform.ASTTransformation: -------------------------------------------------------------------------------- 1 | fnz.ast.FnzAst 2 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mariogarcia/fnz/HEAD/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Functional programming ideas translated to Groovy. Check FNZ site and docs at http://mariogarcia.github.com/fnz 2 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/images/bg.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mariogarcia/fnz/HEAD/fnz-site/src/site/theme/images/bg.jpg -------------------------------------------------------------------------------- /fnz-site/src/site/theme/images/bg_old.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mariogarcia/fnz/HEAD/fnz-site/src/site/theme/images/bg_old.jpg -------------------------------------------------------------------------------- /fnz-site/src/site/theme/images/callout.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mariogarcia/fnz/HEAD/fnz-site/src/site/theme/images/callout.jpg -------------------------------------------------------------------------------- /fnz-site/src/site/theme/images/portfolio-1.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mariogarcia/fnz/HEAD/fnz-site/src/site/theme/images/portfolio-1.jpg -------------------------------------------------------------------------------- /fnz-site/src/site/theme/images/portfolio-2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mariogarcia/fnz/HEAD/fnz-site/src/site/theme/images/portfolio-2.jpg -------------------------------------------------------------------------------- /fnz-site/src/site/theme/images/portfolio-3.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mariogarcia/fnz/HEAD/fnz-site/src/site/theme/images/portfolio-3.jpg -------------------------------------------------------------------------------- /fnz-site/src/site/theme/images/portfolio-4.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mariogarcia/fnz/HEAD/fnz-site/src/site/theme/images/portfolio-4.jpg -------------------------------------------------------------------------------- /fnz-core/src/main/java/fnz/data/Function.java: -------------------------------------------------------------------------------- 1 | package fnz.data; 2 | 3 | /** 4 | * 5 | */ 6 | public interface Function { 7 | B apply(A input); 8 | } 9 | -------------------------------------------------------------------------------- /fnz-core/src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule: -------------------------------------------------------------------------------- 1 | moduleName=fnz 2 | moduleVersion=1.0 3 | extensionClasses=fnz.ast.FnzExtensionModule 4 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mariogarcia/fnz/HEAD/fnz-site/src/site/theme/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /fnz-site/src/site/theme/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mariogarcia/fnz/HEAD/fnz-site/src/site/theme/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /fnz-site/src/site/theme/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mariogarcia/fnz/HEAD/fnz-site/src/site/theme/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mariogarcia/fnz/HEAD/fnz-site/src/site/theme/font-awesome-4.1.0/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mariogarcia/fnz/HEAD/fnz-site/src/site/theme/font-awesome-4.1.0/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mariogarcia/fnz/HEAD/fnz-site/src/site/theme/font-awesome-4.1.0/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mariogarcia/fnz/HEAD/fnz-site/src/site/theme/font-awesome-4.1.0/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /fnz-core/src/main/java/fnz/data/TypeAwareFunction.java: -------------------------------------------------------------------------------- 1 | package fnz.data; 2 | 3 | /** 4 | * 5 | */ 6 | public interface TypeAwareFunction { 7 | B apply(Class resultClazz, A input); 8 | } 9 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/less/fixed-width.less: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .@{fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/scss/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | .#{$fa-css-prefix}-fw { 4 | width: (18em / 14); 5 | text-align: center; 6 | } 7 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/includes/callout.html: -------------------------------------------------------------------------------- 1 | 2 | 7 | -------------------------------------------------------------------------------- /fnz-core/src/main/java/fnz/data/Functor.java: -------------------------------------------------------------------------------- 1 | package fnz.data; 2 | 3 | /** 4 | * 5 | * @param The contained type 6 | */ 7 | public interface Functor { 8 | public > F fmap(Function fn); 9 | } 10 | -------------------------------------------------------------------------------- /fnz-core/src/main/java/fnz/data/TypeIterable.java: -------------------------------------------------------------------------------- 1 | package fnz.data; 2 | 3 | /** 4 | * 5 | */ 6 | public class TypeIterable extends Type> { 7 | 8 | public TypeIterable(Iterable col) { 9 | super(col); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Apr 22 18:48:51 CEST 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.3-bin.zip 7 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/includes/gradle.adoc: -------------------------------------------------------------------------------- 1 | **Fnz** is available at **jcenter**: 2 | 3 | [source,groovy] 4 | ---- 5 | repositories { 6 | jcenter() 7 | } 8 | ---- 9 | 10 | and then add the latest **Fnz** dependency: 11 | 12 | [source,groovy] 13 | ---- 14 | compile 'fnz:fnz:1.0.5' 15 | ---- 16 | 17 | -------------------------------------------------------------------------------- /fnz-core/src/main/java/fnz/data/Type.java: -------------------------------------------------------------------------------- 1 | package fnz.data; 2 | 3 | /** 4 | * 5 | */ 6 | public class Type { 7 | 8 | private final A value; 9 | 10 | public Type(A value) { 11 | this.value = value; 12 | } 13 | 14 | public A getValue() { 15 | return this.value; 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /fnz-core/src/main/java/fnz/data/Applicative.java: -------------------------------------------------------------------------------- 1 | package fnz.data; 2 | 3 | /** 4 | * 5 | * @param The type contained in this container 6 | */ 7 | public interface Applicative extends Functor { 8 | public > U getTypedRef(); 9 | public Applicative fapply(Applicative> afn); 10 | } 11 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/less/core.less: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix} { 5 | display: inline-block; 6 | font-family: FontAwesome; 7 | font-style: normal; 8 | font-weight: normal; 9 | line-height: 1; 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | } 13 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/scss/_core.scss: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix} { 5 | display: inline-block; 6 | font-family: FontAwesome; 7 | font-style: normal; 8 | font-weight: normal; 9 | line-height: 1; 10 | -webkit-font-smoothing: antialiased; 11 | -moz-osx-font-smoothing: grayscale; 12 | } 13 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/includes/grab.adoc: -------------------------------------------------------------------------------- 1 | Try your **Hello world** with **Grab** 2 | 3 | [source, groovy] 4 | ---- 5 | @Grab('fnz:fnz:1.0.5') 6 | @GrabResolver('http://dl.bintray.com/marioggar/maven') 7 | def failure = Try(1) { Integer x -> x / 0 } 8 | 9 | assert !failure.isSuccess() 10 | assert failure.isFailure() 11 | assert failure.or(Success(3)).get() == 3 12 | ---- 13 | -------------------------------------------------------------------------------- /fnz-site/src/site/.cache/markup/asciidoctor.0_1_4/227caa386e2b66a508e4493e7e0c7c6e.html: -------------------------------------------------------------------------------- 1 |
2 |
    3 |
  • 4 |

    secure

    5 |
  • 6 |
  • 7 |

    one

    8 |
  • 9 |
  • 10 |

    two

    11 |
  • 12 |
  • 13 |

    three

    14 |
  • 15 |
16 |
17 |
18 |

la

19 |
20 | 21 |
22 |
-------------------------------------------------------------------------------- /fnz-site/src/site/theme/src/com/sysgears/theme/Launcher.java: -------------------------------------------------------------------------------- 1 | package com.sysgears.theme; 2 | 3 | import com.sysgears.grain.Main; 4 | 5 | /** 6 | * Grain launcher. 7 | */ 8 | public class Launcher { 9 | 10 | /** 11 | * Transfers control to Grain. 12 | * 13 | * @param args command-line arguments 14 | */ 15 | public static void main(final String[] args) { 16 | Main.main(args); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /fnz-site/src/site/.cache/markup/asciidoctor.0_1_4/1ea05eebc5bf384fe3648ecba981dfe8.html: -------------------------------------------------------------------------------- 1 |
2 |
    3 |
  • 4 |

    secure

    5 |
  • 6 |
  • 7 |

    one

    8 |
  • 9 |
  • 10 |

    two

    11 |
  • 12 |
  • 13 |

    three

    14 |
  • 15 |
16 |
17 |
18 |

la

19 |
20 |
21 |

false

22 |
23 |
24 |
-------------------------------------------------------------------------------- /fnz-site/src/site/.cache/markup/asciidoctor.0_1_4/3ba0ba7edd892221e3a9404de11927d7.html: -------------------------------------------------------------------------------- 1 |
2 |
    3 |
  • 4 |

    secure

    5 |
  • 6 |
  • 7 |

    one

    8 |
  • 9 |
  • 10 |

    two

    11 |
  • 12 |
  • 13 |

    three

    14 |
  • 15 |
16 |
17 |
18 |

la

19 |
20 |
21 |

true

22 |
23 |
24 |
-------------------------------------------------------------------------------- /fnz-site/src/site/.cache/markup/asciidoctor.0_1_4/77fb01029d18c2753d1f73ff6afb821c.html: -------------------------------------------------------------------------------- 1 |
2 |
    3 |
  • 4 |

    secure

    5 |
  • 6 |
  • 7 |

    one

    8 |
  • 9 |
  • 10 |

    two

    11 |
  • 12 |
  • 13 |

    three

    14 |
  • 15 |
16 |
17 |
18 |

la

19 |
20 |
21 |

.

22 |
23 |
24 |
-------------------------------------------------------------------------------- /fnz-site/src/site/theme/includes/about.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |
6 |

${page.about.title}

7 |

${page.about.text}

8 |
9 |
10 | 11 |
12 | 13 |
14 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/less/bordered-pulled.less: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em @fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .pull-right { float: right; } 11 | .pull-left { float: left; } 12 | 13 | .@{fa-css-prefix} { 14 | &.pull-left { margin-right: .3em; } 15 | &.pull-right { margin-left: .3em; } 16 | } 17 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/less/rotated-flipped.less: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-rotate-90 { .fa-icon-rotate(90deg, 1); } 5 | .@{fa-css-prefix}-rotate-180 { .fa-icon-rotate(180deg, 2); } 6 | .@{fa-css-prefix}-rotate-270 { .fa-icon-rotate(270deg, 3); } 7 | 8 | .@{fa-css-prefix}-flip-horizontal { .fa-icon-flip(-1, 1, 0); } 9 | .@{fa-css-prefix}-flip-vertical { .fa-icon-flip(1, -1, 2); } 10 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/scss/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em $fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .pull-right { float: right; } 11 | .pull-left { float: left; } 12 | 13 | .#{$fa-css-prefix} { 14 | &.pull-left { margin-right: .3em; } 15 | &.pull-right { margin-left: .3em; } 16 | } 17 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/includes/basics.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |
5 |
6 |

Getting Started

7 |

Let's review some basic examples:

8 |
9 |
10 | 11 |
12 | 13 |
14 | -------------------------------------------------------------------------------- /fnz-site/src/site/.cache/markup/asciidoctor.0_1_4/a0324a8707242c86cc5dfe0186ee19b4.html: -------------------------------------------------------------------------------- 1 |
2 |
    3 |
  • 4 |

    secure

    5 |
  • 6 |
  • 7 |

    one

    8 |
  • 9 |
  • 10 |

    two

    11 |
  • 12 |
  • 13 |

    three

    14 |
  • 15 |
16 |
17 |
18 |

la

19 |
20 |
21 |

../src/test/groovy/fnz/FnzSpec.groovy

22 |
23 |
24 |
-------------------------------------------------------------------------------- /fnz-site/src/site/.cache/markup/asciidoctor.0_1_4/e5673777036cb049a2dd306bf2b5a0c6.html: -------------------------------------------------------------------------------- 1 |
2 |
    3 |
  • 4 |

    secure

    5 |
  • 6 |
  • 7 |

    one

    8 |
  • 9 |
  • 10 |

    two

    11 |
  • 12 |
  • 13 |

    three

    14 |
  • 15 |
16 |
17 |
-------------------------------------------------------------------------------- /fnz-site/src/site/.cache/markup/asciidoctor.0_1_4/ab6dba8f85b9c4699537fb0a10636e47.html: -------------------------------------------------------------------------------- 1 |
2 |
    3 |
  • 4 |

    secure

    5 |
  • 6 |
  • 7 |

    one

    8 |
  • 9 |
  • 10 |

    two

    11 |
  • 12 |
  • 13 |

    three

    14 |
  • 15 |
16 |
17 |
18 |

la

19 |
20 |
21 |

/home/mario/Development/ws/fnz/src/site/.

22 |
23 |
24 |
-------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/less/larger.less: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .@{fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .@{fa-css-prefix}-2x { font-size: 2em; } 11 | .@{fa-css-prefix}-3x { font-size: 3em; } 12 | .@{fa-css-prefix}-4x { font-size: 4em; } 13 | .@{fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/scss/_larger.scss: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | .#{$fa-css-prefix}-lg { 6 | font-size: (4em / 3); 7 | line-height: (3em / 4); 8 | vertical-align: -15%; 9 | } 10 | .#{$fa-css-prefix}-2x { font-size: 2em; } 11 | .#{$fa-css-prefix}-3x { font-size: 3em; } 12 | .#{$fa-css-prefix}-4x { font-size: 4em; } 13 | .#{$fa-css-prefix}-5x { font-size: 5em; } 14 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/layouts/default.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | ${include "head.html"} 4 | 5 | ${include "navigation.html"} 6 | ${include "header.html"} 7 | ${content} 8 | ${include "about.html"} 9 | ${include "configuration.html"} 10 | ${include "basics.html"} 11 | ${include "basics-samples.html"} 12 | ${include "checkdocumentation.html"} 13 | ${include "javascripts.html"} 14 | 15 | 16 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/less/list.less: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: @fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .@{fa-css-prefix}-li { 11 | position: absolute; 12 | left: -@fa-li-width; 13 | width: @fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.@{fa-css-prefix}-lg { 17 | left: -@fa-li-width + (4em / 14); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/scss/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } 5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } 6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } 7 | 8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } 9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } 10 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/scss/_list.scss: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-ul { 5 | padding-left: 0; 6 | margin-left: $fa-li-width; 7 | list-style-type: none; 8 | > li { position: relative; } 9 | } 10 | .#{$fa-css-prefix}-li { 11 | position: absolute; 12 | left: -$fa-li-width; 13 | width: $fa-li-width; 14 | top: (2em / 14); 15 | text-align: center; 16 | &.#{$fa-css-prefix}-lg { 17 | left: -$fa-li-width + (4em / 14); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/scss/font-awesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.1.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables"; 7 | @import "mixins"; 8 | @import "path"; 9 | @import "core"; 10 | @import "larger"; 11 | @import "fixed-width"; 12 | @import "list"; 13 | @import "bordered-pulled"; 14 | @import "spinning"; 15 | @import "rotated-flipped"; 16 | @import "stacked"; 17 | @import "icons"; 18 | -------------------------------------------------------------------------------- /fnz-site/src/docs/index.ad: -------------------------------------------------------------------------------- 1 | = FNZ 2 | Mario Garcia 3 | :numbered: 4 | :testDir: ../../../fnz-core/src/test/groovy 5 | :sourceDir: ../../../fnz-core/src/main/groovy 6 | :sourceDirJava: ../../../fnz-core/src/main/java 7 | :stem: 8 | 9 | Functional Groovy 10 | 11 | include::intro.ad[] 12 | 13 | include::data_theory.ad[] 14 | 15 | include::type.ad[] 16 | 17 | include::data.ad[] 18 | 19 | include::control.ad[] 20 | 21 | 22 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/less/font-awesome.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.1.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables.less"; 7 | @import "mixins.less"; 8 | @import "path.less"; 9 | @import "core.less"; 10 | @import "larger.less"; 11 | @import "fixed-width.less"; 12 | @import "list.less"; 13 | @import "bordered-pulled.less"; 14 | @import "spinning.less"; 15 | @import "rotated-flipped.less"; 16 | @import "stacked.less"; 17 | @import "icons.less"; 18 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/includes/calltoaction.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 14 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/less/stacked.less: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .@{fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .@{fa-css-prefix}-stack-1x, .@{fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .@{fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .@{fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .@{fa-css-prefix}-inverse { color: @fa-inverse; } 21 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/scss/_stacked.scss: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; } 21 | -------------------------------------------------------------------------------- /fnz-core/src/main/java/fnz/data/Truth.java: -------------------------------------------------------------------------------- 1 | package fnz.data; 2 | 3 | /** 4 | * All classes implementing this inteface will be capable of 5 | * participate of the Groovy truth. 6 | * 7 | */ 8 | public interface Truth { 9 | 10 | /** 11 | * This method has to be implemented to know whether the 12 | * object implementing this interface should be treated 13 | * as a Boolean.TRUE value or Boolean.FALSE 14 | * 15 | * This interface has been created in order to 16 | * make it easier to interact with Groovy collections 17 | * and participate of the Groovy truth 18 | * 19 | * @return the boolean representation of the current type 20 | */ 21 | public Boolean asBoolean(); 22 | 23 | } 24 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/includes/header.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 |

${page.header.title}

5 |

${page.header.subtitle}

6 | 14 |
15 | ${page.header.button} 16 |
17 |
18 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/includes/navigation.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/less/path.less: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: ~"url('@{fa-font-path}/fontawesome-webfont.eot?v=@{fa-version}')"; 7 | src: ~"url('@{fa-font-path}/fontawesome-webfont.eot?#iefix&v=@{fa-version}') format('embedded-opentype')", 8 | ~"url('@{fa-font-path}/fontawesome-webfont.woff?v=@{fa-version}') format('woff')", 9 | ~"url('@{fa-font-path}/fontawesome-webfont.ttf?v=@{fa-version}') format('truetype')", 10 | ~"url('@{fa-font-path}/fontawesome-webfont.svg?v=@{fa-version}#fontawesomeregular') format('svg')"; 11 | // src: url('@{fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 12 | font-weight: normal; 13 | font-style: normal; 14 | } 15 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/scss/_path.scss: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); 7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), 8 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), 9 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), 10 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); 11 | //src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 12 | font-weight: normal; 13 | font-style: normal; 14 | } 15 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/less/mixins.less: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | .fa-icon-rotate(@degrees, @rotation) { 5 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation); 6 | -webkit-transform: rotate(@degrees); 7 | -moz-transform: rotate(@degrees); 8 | -ms-transform: rotate(@degrees); 9 | -o-transform: rotate(@degrees); 10 | transform: rotate(@degrees); 11 | } 12 | 13 | .fa-icon-flip(@horiz, @vert, @rotation) { 14 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=@rotation, mirror=1); 15 | -webkit-transform: scale(@horiz, @vert); 16 | -moz-transform: scale(@horiz, @vert); 17 | -ms-transform: scale(@horiz, @vert); 18 | -o-transform: scale(@horiz, @vert); 19 | transform: scale(@horiz, @vert); 20 | } 21 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/font-awesome-4.1.0/scss/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | @mixin fa-icon-rotate($degrees, $rotation) { 5 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}); 6 | -webkit-transform: rotate($degrees); 7 | -moz-transform: rotate($degrees); 8 | -ms-transform: rotate($degrees); 9 | -o-transform: rotate($degrees); 10 | transform: rotate($degrees); 11 | } 12 | 13 | @mixin fa-icon-flip($horiz, $vert, $rotation) { 14 | filter: progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}); 15 | -webkit-transform: scale($horiz, $vert); 16 | -moz-transform: scale($horiz, $vert); 17 | -ms-transform: scale($horiz, $vert); 18 | -o-transform: scale($horiz, $vert); 19 | transform: scale($horiz, $vert); 20 | } 21 | -------------------------------------------------------------------------------- /fnz-core/src/test/groovy/fnz/ast/flow/UnlessSpecExample.groovy: -------------------------------------------------------------------------------- 1 | package fnz.ast.flow 2 | 3 | import static fnz.Fnz.val 4 | import fnz.data.Maybe 5 | 6 | class UnlessSpecExample { 7 | 8 | Integer basicUnlessExample(Integer xparam) { 9 | // tag::simpleUnless[] 10 | Maybe result = 11 | unless(xparam <= 0) { 12 | return 3 + xparam 13 | } 14 | // end::simpleUnless[] 15 | return val(result) 16 | } 17 | 18 | Integer nestedUnlessExample(Integer xparam) { 19 | Maybe result = 20 | unless(xparam == 2) { 21 | def nested = 22 | unless(xparam == 1) { 23 | 3 + xparam 24 | } 25 | val(nested) 26 | } 27 | 28 | return val(result) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/src/com/sysgears/theme/taglib/ThemeTagLib.groovy: -------------------------------------------------------------------------------- 1 | package com.sysgears.theme.taglib 2 | 3 | import com.sysgears.grain.taglib.GrainTagLib 4 | 5 | class ThemeTagLib { 6 | 7 | /** 8 | * Grain taglib reference. 9 | */ 10 | private GrainTagLib taglib 11 | 12 | public ThemeTagLib(GrainTagLib taglib) { 13 | this.taglib = taglib 14 | } 15 | 16 | /** 17 | * Converts a date to XML date time format: 2013-12-31T12:49:00+07:00 18 | * 19 | * @attr date the date to convert 20 | */ 21 | def xmlDateTime = { Map model -> 22 | if (!model.date) throw new IllegalArgumentException('Tag [xmlDateTime] is missing required attribute [date]') 23 | 24 | def tz = String.format('%tz', model.date) 25 | 26 | String.format("%tFT%=', 0, 0), 19 | type 20 | ) 21 | then: 'the method should respond properly' 22 | result == correct 23 | where: 'possible values are' 24 | type | correct 25 | Types.COMPARE_GREATER_THAN_EQUAL | true 26 | Types.COMPARE_GREATER_THAN | false 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /fnz-core/src/main/groovy/fnz/ast/FnzAst.groovy: -------------------------------------------------------------------------------- 1 | package fnz.ast 2 | 3 | import groovy.transform.CompileStatic 4 | import org.codehaus.groovy.control.CompilePhase 5 | import org.codehaus.groovy.transform.GroovyASTTransformation 6 | 7 | import fnz.ast.flow.UnlessAstTransformer 8 | import fnz.ast.flow.LetmAstTransformer 9 | import fnz.ast.flow.LetAstTransformer 10 | import fnz.ast.flow.DoAstTransformer 11 | 12 | import fnz.ast.type.FuncAstTransformer 13 | 14 | /** 15 | * This AST applies all transformations available in FNZ. 16 | * 17 | * @since 1.0.1 18 | * 19 | */ 20 | @CompileStatic 21 | @GroovyASTTransformation(phase = CompilePhase.CONVERSION) 22 | class FnzAst extends AstExpressionTransformerAware { 23 | 24 | @Override 25 | List getTransformers() { 26 | return [ 27 | UnlessAstTransformer, 28 | LetmAstTransformer, 29 | LetAstTransformer, 30 | FuncAstTransformer, 31 | DoAstTransformer 32 | ] 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /fnz-core/src/test/groovy/fnz/ast/flow/LetSpecExample.groovy: -------------------------------------------------------------------------------- 1 | package fnz.ast.flow 2 | 3 | @SuppressWarnings('SpaceBeforeOpeningBrace') 4 | class LetSpecExample { 5 | 6 | // tag::simpleLet[] 7 | Integer simpleLetExample() { 8 | let(x:1, y:2, z:3) { 9 | x + y + z 10 | } 11 | } 12 | // end::simpleLet[] 13 | 14 | // tag::computedValues[] 15 | Integer computedValues() { 16 | let(x:1, y:2, z:{ x + y }) { 17 | x + y + z 18 | } 19 | } 20 | // end::computedValues[] 21 | 22 | // tag::nestedLet[] 23 | Integer nestedLetExample() { 24 | let(a:5, b:15) { 25 | let(c:15, d:{ a + 25 }) { 26 | b + c + d 27 | } 28 | } 29 | } 30 | // end::nestedLet[] 31 | 32 | // tag::resolveMethodVariables[] 33 | Integer sumAndInc(Integer x, Integer y) { 34 | let(a:x, b:y) { 35 | a + b + 1 36 | } 37 | } 38 | // end::resolveMethodVariables[] 39 | 40 | } 41 | -------------------------------------------------------------------------------- /fnz-core/src/main/java/fnz/data/MonadType.java: -------------------------------------------------------------------------------- 1 | package fnz.data; 2 | 3 | public abstract class MonadType implements Monad, Truth { 4 | 5 | private static final String EMPTY = ""; 6 | 7 | private final Type typedRef; 8 | 9 | protected MonadType(Type valueRef) { 10 | this.typedRef = valueRef; 11 | } 12 | 13 | @Override 14 | public Type getTypedRef() { 15 | return this.typedRef; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return toStringSimple(this); 21 | } 22 | 23 | private String toStringSimple(Monad monad) { 24 | String simpleName = monad.getClass().getSimpleName(); 25 | A monadValue = monad.getTypedRef().getValue(); 26 | String valueToStr = monadValue != null ? monadValue.toString() : EMPTY; 27 | 28 | return simpleName + "(" + valueToStr + ")"; 29 | } 30 | 31 | @Override 32 | public U get() { 33 | return (U) this.getTypedRef().getValue(); 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/includes/checkdocumentation.html: -------------------------------------------------------------------------------- 1 | 2 |
3 |
4 | 9 |
10 |
11 |

Or checkout the code at Github

12 |
    13 |
  • 14 | 15 |
  • 16 |
17 |
18 |

${page.footer.copyright}

19 |
20 |
21 | 22 |
23 | 24 |
25 | -------------------------------------------------------------------------------- /fnz-core/src/main/groovy/fnz/ast/AstExpressionTransformerAware.groovy: -------------------------------------------------------------------------------- 1 | package fnz.ast 2 | 3 | import org.codehaus.groovy.ast.ASTNode 4 | import org.codehaus.groovy.ast.ClassNode 5 | import org.codehaus.groovy.control.SourceUnit 6 | import org.codehaus.groovy.transform.AbstractASTTransformation 7 | 8 | /** 9 | * This class applies all transformers provided by the method 10 | * getTransformers in order to all class nodes present in 11 | * a given source unit instance. 12 | * 13 | * @since 1.0.3 14 | * 15 | */ 16 | abstract class AstExpressionTransformerAware extends AbstractASTTransformation { 17 | 18 | @SuppressWarnings('UnusedMethodParameter') 19 | void visit(ASTNode[] nodes, SourceUnit sourceUnit) { 20 | List classNodeList = sourceUnit.AST.classes.collect() 21 | 22 | classNodeList.each { ClassNode clazzNode -> 23 | transformers.each { Class clazz -> 24 | clazz.newInstance(sourceUnit).visitClass(clazzNode) 25 | } 26 | } 27 | } 28 | 29 | abstract List getTransformers() 30 | 31 | } 32 | -------------------------------------------------------------------------------- /fnz-core/src/test/groovy/fnz/data/FunctionSpec.groovy: -------------------------------------------------------------------------------- 1 | package fnz.data 2 | 3 | import groovy.transform.CompileStatic 4 | import spock.lang.Specification 5 | 6 | /** 7 | * 8 | */ 9 | @CompileStatic 10 | class FunctionSpec extends Specification { 11 | 12 | // tag::function1[] 13 | void 'Building a simple function'() { 14 | given: 'a function instance using closures' 15 | Function plus_3 = { Integer v -> 16 | v + 3 17 | } as Function 18 | when: 'using it' 19 | Integer result = plus_3.apply(5) 20 | then: 'the value to be the number plus 3' 21 | result == 8 22 | } 23 | // end::function1[] 24 | 25 | // tag::function2[] 26 | void 'Composing function instances'() { 27 | given: 'a function instance using closures' 28 | def plus_3 = { Integer v -> v + 3 } as Function 29 | def plus_4 = { Integer v -> v + 4 } as Function 30 | when: 'using it' 31 | Integer result = plus_3.apply(plus_4.apply(5)) 32 | then: 'the value to be the number plus 3' 33 | result == 12 34 | } 35 | // end::function2[] 36 | } 37 | 38 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/includes/try.adoc: -------------------------------------------------------------------------------- 1 | The `Try` abstraction is a different way of dealing with exceptions in 2 | your apps. Instead of writing a **try-catch** block just make your 3 | computation to return a value. Basically if an exception is thrown 4 | you'll get a `fnz.data.Try.Failure` instance that will contain both 5 | **the value** that produced the exception and **the exception** itself. 6 | 7 | On the other hand if everything goes ok you'll get a 8 | `fnz.data.Try.Success` instance with the value produced by the 9 | computation. 10 | 11 | In the following example we're using the `Try(T,Function)` from 12 | class **fnz.Fnz** function. This function receives a value and a 13 | function. Because the example is Groovy we normally use a `Closure` but if 14 | you're using Java you could be using an instance of 15 | `fnz.data.Function`. 16 | 17 | [source,groovy] 18 | ---- 19 | include::../test/groovy/TryExampleSpec.groovy[tags=tryBasic1, indent=0] 20 | ---- 21 | 22 | Please notice that the second parameter of a **Try** function is a 23 | function itself (A closure representing a function) so you can 24 | also pass a method reference 25 | 26 | [source,groovy] 27 | ---- 28 | include::../test/groovy/TryExampleSpec.groovy[tags=tryFunction, indent=0] 29 | ---- 30 | -------------------------------------------------------------------------------- /fnz-core/src/main/java/fnz/data/Or.java: -------------------------------------------------------------------------------- 1 | package fnz.data; 2 | 3 | /** 4 | * All types implementing this interface should implement the method 5 | * or(). This method allows to have a positive alternative to the current 6 | * monadic value. 7 | * 8 | */ 9 | public interface Or> { 10 | 11 | /** 12 | * If the semantics of the current monadic value indicate 13 | * that is not valid then the method should return the value 14 | * passed as parameter, otherwise it should return itself 15 | * 16 | * @param monad possible alternative to the current value 17 | * @return current value if valud otherwise it will return 18 | * the value passed as parameter 19 | */ 20 | public M or(M monad); 21 | 22 | /** 23 | * If the semantics of the current monadic value indicate 24 | * that is not valid then the method should return the value 25 | * computed by the function passed as parameter , 26 | * otherwise it should return itself 27 | * 28 | * @param monadGenerator possible alternative computation to the current value 29 | * @return current value if valid otherwise it will return 30 | * the value passed as parameter 31 | */ 32 | public M or(Function monadGenerator); 33 | 34 | } 35 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/includes/head.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | ${page.title} 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/includes/javascripts.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 38 | -------------------------------------------------------------------------------- /fnz-site/src/test/groovy/TryExampleSpec.groovy: -------------------------------------------------------------------------------- 1 | // tag::importStaticTry[] 2 | import static fnz.Fnz.Try 3 | // end::importStaticTry[] 4 | import spock.lang.Specification 5 | 6 | class TryExampleSpec extends Specification { 7 | 8 | // tag::tryBasic1[] 9 | void 'get result from an unsafe operation'() { 10 | when: 'trying to apply a value to an unsafe operation' 11 | // 'two' and 'four' will throw an Exception! 12 | def numbers = ['1', 'two', '3', 'four'] 13 | .collect { String n -> Try(n) { it.toInteger() } } 14 | .findAll() // Using Groovy Truth 15 | 16 | then: 'we should get the successful values' 17 | numbers*.get() == [1, 3] // Failure results have been filtered 18 | 19 | } 20 | // end::tryBasic1[] 21 | 22 | void 'get result from an unsafe operation'() { 23 | when: 'trying to apply a value to an unsafe operation' 24 | def numbers = ['1', 'two', '3', 'four'] 25 | // tag::tryFunction[] 26 | .collect { String n -> Try(n, Integer.&parseInt) } 27 | // end::tryFunction[] 28 | .findAll() // Success and Failure know about Groovy truth 29 | 30 | then: 'we should get the successful values' 31 | numbers*.get() == [1, 3] // Failure results have been filtered 32 | // end::tryBasic1[] 33 | } 34 | 35 | } 36 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/includes/configuration.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 |
6 |
7 |

Grab

8 |
9 |
10 | ${include "grab.adoc"} 11 |
12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 |

Gradle

20 |
21 |
22 | ${include "gradle.adoc"} 23 |
24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 |
32 | Check the examples 33 |
34 |
35 | 36 |
37 | 38 |
39 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/includes/basics-samples.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 |
5 |
6 |
7 |

Maybe

8 |
9 |
10 | ${include "maybe.adoc"} 11 |
12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 |

Try

20 |
21 |
22 | ${include "try.adoc"} 23 |
24 |
25 | 26 |
27 | 28 |
29 |
30 |
31 |
32 | Still interested ? 33 |
34 |
35 | 36 |
37 | 38 |
39 | -------------------------------------------------------------------------------- /fnz-core/src/test/groovy/fnz/ast/flow/UnlessSpec.groovy: -------------------------------------------------------------------------------- 1 | package fnz.ast.flow 2 | 3 | import fnz.ast.FnzAst 4 | 5 | import fnz.test.AstBaseSpec 6 | import spock.lang.Unroll 7 | import org.codehaus.groovy.control.CompilePhase 8 | 9 | class UnlessSpec extends AstBaseSpec { 10 | 11 | void 'simple unless example'() { 12 | given: 'an instance of the example' 13 | def instance = 14 | getClassToTestForPhase(FnzAst, CompilePhase.CANONICALIZATION) 15 | .newInstance() 16 | when: 'executing a unless statement' 17 | Integer result = instance.basicUnlessExample(sample) 18 | then: 'we should get the proper result' 19 | result == expected 20 | where: 21 | sample | expected 22 | 1 | 4 23 | 0 | null 24 | -1 | null 25 | 2 | 5 26 | 3 | 6 27 | } 28 | 29 | @Unroll 30 | void 'nested unless expressions'() { 31 | given: 'an instance of the example' 32 | def instance = 33 | getClassToTestForPhase(FnzAst, CompilePhase.CANONICALIZATION) 34 | .newInstance() 35 | when: 'executing a unless statement' 36 | Integer result = instance.nestedUnlessExample(sample) 37 | then: 'we should get the proper result' 38 | result == expected 39 | where: 40 | sample | expected 41 | 1 | null 42 | 2 | null 43 | 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/src/com/sysgears/theme/ResourceMapper.groovy: -------------------------------------------------------------------------------- 1 | package com.sysgears.theme 2 | 3 | import com.sysgears.grain.taglib.Site 4 | 5 | /** 6 | * Change pages urls and extend models. 7 | */ 8 | class ResourceMapper { 9 | 10 | /** 11 | * Site reference, provides access to site configuration. 12 | */ 13 | private final Site site 14 | 15 | public ResourceMapper(Site site) { 16 | this.site = site 17 | } 18 | 19 | /** 20 | * This closure is used to transform page URLs and page data models. 21 | */ 22 | def map = { resources -> 23 | 24 | def refinedResources = resources.findResults(filterPublished).collect { Map resource -> 25 | fillDates << resource 26 | } 27 | 28 | refinedResources 29 | } 30 | 31 | /** 32 | * Excludes resources with published property set to false, 33 | * unless it is allowed to show unpublished resources in SiteConfig. 34 | */ 35 | private def filterPublished = { Map it -> 36 | (it.published != false || site.show_unpublished) ? it : null 37 | } 38 | 39 | /** 40 | * Fills in page `date` and `updated` fields 41 | */ 42 | private def fillDates = { Map it -> 43 | def update = [date: it.date ? Date.parse(site.datetime_format, it.date) : new Date(it.dateCreated as Long), 44 | updated: it.updated ? Date.parse(site.datetime_format, it.updated) : new Date(it.lastUpdated as Long)] 45 | it + update 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /fnz-core/src/main/groovy/fnz/ast/flow/DoReturnAstTransformer.groovy: -------------------------------------------------------------------------------- 1 | package fnz.ast.flow 2 | 3 | import static org.codehaus.groovy.ast.tools.GeneralUtils.varX 4 | import static org.codehaus.groovy.ast.tools.GeneralUtils.callX 5 | 6 | import fnz.ast.MethodCallExpressionTransformer 7 | 8 | import org.codehaus.groovy.control.SourceUnit 9 | 10 | import org.codehaus.groovy.ast.expr.Expression 11 | import org.codehaus.groovy.ast.expr.MethodCallExpression 12 | 13 | class DoReturnAstTransformer extends MethodCallExpressionTransformer { 14 | 15 | static final String DO_RETURN_METHOD_NAME = '$return' 16 | static final String UNIT_METHOD_NAME = 'unit' 17 | 18 | private final String monadClassId 19 | 20 | /** 21 | * The $return method only does a call to the monad's unit(v) function. 22 | * In order to acomplish that it needs to know which is the name of the 23 | * monad variable in the available scope. 24 | * 25 | * @param monadClassId the name of the monad variable 26 | * @param sourceUnit 27 | */ 28 | DoReturnAstTransformer(String monadClassId, SourceUnit sourceUnit) { 29 | super(sourceUnit, DO_RETURN_METHOD_NAME) 30 | this.monadClassId = monadClassId 31 | } 32 | 33 | @Override 34 | Expression transformMethodCall(final MethodCallExpression methodCallExpression) { 35 | return callX( 36 | varX(monadClassId), 37 | UNIT_METHOD_NAME, 38 | methodCallExpression.arguments 39 | ) 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /fnz-core/src/test/groovy/fnz/ast/flow/LetSpec.groovy: -------------------------------------------------------------------------------- 1 | package fnz.ast.flow 2 | 3 | import fnz.ast.FnzAst 4 | 5 | import fnz.test.AstBaseSpec 6 | import org.codehaus.groovy.control.CompilePhase 7 | 8 | class LetSpec extends AstBaseSpec { 9 | 10 | def exampleInstance 11 | 12 | def setup() { 13 | exampleInstance = 14 | getClassToTestForPhase( 15 | FnzAst, 16 | CompilePhase.CONVERSION).newInstance() 17 | } 18 | 19 | def 'simple let'() { 20 | when: 'Initializing expression and executing closure' 21 | Integer result = exampleInstance.simpleLetExample() 22 | then: 'The value should be the expected' 23 | result == 6 24 | } 25 | 26 | def 'nested let'() { 27 | when: 'Initializing expression and executing closure' 28 | Integer result = exampleInstance.nestedLetExample() 29 | then: 'The value should be the expected' 30 | result == 60 31 | } 32 | 33 | def 'computed values'() { 34 | when: 'Initializing expression and executing closure' 35 | Integer result = exampleInstance.computedValues() 36 | then: 'The value should be the expected' 37 | result == 6 38 | } 39 | 40 | def 'resolve variables'() { 41 | when: 'Initializing expression and executing closure' 42 | Integer result = exampleInstance.sumAndInc(1, 2) 43 | then: 'The value should be the expected' 44 | result == 4 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /fnz-core/src/test/groovy/fnz/test/AstBaseSpec.groovy: -------------------------------------------------------------------------------- 1 | package fnz.test 2 | 3 | import static org.codehaus.groovy.control.CompilePhase.INSTRUCTION_SELECTION 4 | 5 | import spock.lang.Specification 6 | import org.codehaus.groovy.control.CompilePhase 7 | import org.codehaus.groovy.tools.ast.TransformTestHelper 8 | 9 | /** 10 | * 11 | * @author marioggar 12 | **/ 13 | class AstBaseSpec extends Specification{ 14 | 15 | static final BASE = "./src/test/groovy/" 16 | 17 | /** 18 | * This method helps to create a new class instance to be able to 19 | * test the class that uses the transformation 20 | * 21 | * @param transformationClass The name of the AST transformation we want to test 22 | * @return a class of the class that contains the transformation 23 | **/ 24 | def getClassToTest(transformationClass){ 25 | getClassToTestForPhase(transformationClass, CompilePhase.INSTRUCTION_SELECTION) 26 | } 27 | 28 | def getClassToTestForPhase(Class transformationClass, CompilePhase compilePhase) { 29 | TransformTestHelper invoker = 30 | getScriptParser(transformationClass, compilePhase) 31 | 32 | def qualifiedName = getClass().name.replaceAll("\\.", "\\/") 33 | def file = new File("${BASE}${qualifiedName}Example.groovy") 34 | 35 | /* The class we want to test */ 36 | return invoker.parse(file) 37 | } 38 | 39 | TransformTestHelper getScriptParser(Class transformationClass, CompilePhase compilePhase) { 40 | return new TransformTestHelper( 41 | transformationClass.newInstance(), 42 | compilePhase 43 | ) 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /fnz-core/src/codenarc/rules.groovy: -------------------------------------------------------------------------------- 1 | ruleset { 2 | description 'SeeQuestor RuleSet' 3 | 4 | ruleset('rulesets/basic.xml') 5 | ruleset('rulesets/exceptions.xml') 6 | ruleset('rulesets/imports.xml') 7 | ruleset('rulesets/unused.xml') { 8 | 'UnusedMethodParameter' { 9 | doNotApplyToClassNames = '*ExtensionModule' 10 | } 11 | } 12 | 13 | ruleset('rulesets/dry.xml') { 14 | 'DuplicateNumberLiteral' { 15 | doNotApplyToClassNames = '*Spec,*SpecExample' 16 | } 17 | 'DuplicateStringLiteral' { 18 | doNotApplyToClassNames = '*Spec,*SpecExample' 19 | } 20 | 'DuplicateMapLiteral' { 21 | doNotApplyToClassNames = '*Spec,*SpecExample' 22 | } 23 | 'DuplicateListLiteral' { 24 | doNotApplyToClassNames = '*Spec,*SpecExample' 25 | } 26 | } 27 | 28 | ruleset('rulesets/formatting.xml') { 29 | 'SpaceBeforeOpeningBrace' { 30 | doNotApplyToClassNames = '*Spec,*SpecExample' 31 | } 32 | 'ClassJavadoc' { 33 | enabled = false 34 | } 35 | 'FileEndsWithoutNewline' { 36 | enabled = false 37 | } 38 | } 39 | 40 | ruleset('rulesets/naming.xml') { 41 | 'MethodName' { 42 | doNotApplyToClassNames = '*Spec,*ExtensionModule' 43 | } 44 | 'VariableName' { 45 | doNotApplyToClassNames = '*Spec,*SpecExample' 46 | } 47 | 'FactoryMethodName' { 48 | doNotApplyToClassNames = '*Spec,*SpecExample' 49 | } 50 | } 51 | 52 | ruleset('rulesets/convention.xml') 53 | 54 | } 55 | -------------------------------------------------------------------------------- /fnz-core/src/main/java/fnz/data/Monad.java: -------------------------------------------------------------------------------- 1 | package fnz.data; 2 | 3 | /** 4 | * 5 | * @param The contained type 6 | */ 7 | public interface Monad extends Applicative { 8 | 9 | public > M bind(Function fn); 10 | 11 | /** 12 | * The behavior is the same as the bind(Function<A,M>) method but with type awareness. 13 | *
14 | * This method was created exclusively for type inference for the $do/$return expressions. 15 | * For instance in an expression like the following: 16 | *
17 |      *     $do {
18 |      *         x = Just(1)
19 |      *         y = Just(x + 1)
20 |      *
21 |      *         $return x + y
22 |      *     }
23 |      * 
24 | * The expression is translated into this: 25 | *
26 |      *     Just(1).bind { _clazz1231, x ->
27 |      *         Just(x + 1).bind { _clazz2324, y ->
28 |      *             _clazz2324.unit(y)
29 |      *         }
30 |      *     }
31 |      * 
32 | * That way we don't have to specify the type of the Monad needed to 33 | * end the expression, the function itself carries the type. 34 | * 35 | * @param The type contained in the monadic result 36 | * @param the result monadic type 37 | * @param fn a function aware of the type of the returned monad 38 | * @return a monad as a result of the function execution 39 | * 40 | */ 41 | public > M bind2(TypeAwareFunction fn); 42 | 43 | /** 44 | * This method is a helper to return the real value wrapped in 45 | * this container 46 | * 47 | * @return an instance of T 48 | * @param the type you want to return 49 | */ 50 | public U get(); 51 | 52 | } 53 | -------------------------------------------------------------------------------- /fnz-core/src/test/groovy/fnz/ast/flow/LetmSpecExample.groovy: -------------------------------------------------------------------------------- 1 | package fnz.ast.flow 2 | 3 | import static fnz.Fnz.Just 4 | import static fnz.Fnz.Right 5 | import static fnz.Fnz.Left 6 | import static fnz.Fnz.wrap 7 | import static fnz.Fnz.bind 8 | 9 | import fnz.data.Try 10 | import fnz.data.Either 11 | import fnz.data.Maybe 12 | 13 | class LetmSpecExample { 14 | 15 | Maybe simpleLetmExpression() { 16 | return letm(a:Just(10), 17 | b:Just(20), 18 | c:{ Just(a + b) }) { 19 | 20 | Just(c) 21 | } 22 | } 23 | 24 | Maybe nestingLetms() { 25 | letm(a:Just(10), 26 | b:Just(20)) { 27 | letm(d:Just(6), 28 | e:{ Just(d + 1) }) { 29 | 30 | def result = a + b + d + e 31 | 32 | Just(result) 33 | } 34 | } 35 | } 36 | 37 | // tag::workingWithOtherMonads[] 38 | Either workingWithOtherMonads(final Integer first, final Integer second) { 39 | return letm(x:Just(first), 40 | y:Just(second)) { 41 | letm(result:Just(first + second)) { 42 | result == 3 ? Right(result) : Left() 43 | } 44 | } 45 | } 46 | // end::workingWithOtherMonads[] 47 | 48 | // tag::combineWithTry[] 49 | Try combineWithTry(Integer first, Integer second) { 50 | letm(x:Just(first), 51 | y:Just(second)) { 52 | bind(Just(second), wrap(this.&dangerousMethod.curry(first))) 53 | } 54 | } 55 | 56 | Integer dangerousMethod(Integer x, Integer y) { 57 | return x / y 58 | } 59 | // end::combineWithTry[] 60 | 61 | } 62 | -------------------------------------------------------------------------------- /fnz-site/src/test/groovy/MaybeExampleSpec.groovy: -------------------------------------------------------------------------------- 1 | // tag::importStaticTry[] 2 | import fnz.Fnz 3 | // end::importStaticTry[] 4 | import spock.lang.Specification 5 | 6 | class MaybeExampleSpec extends Specification { 7 | 8 | void 'handling NPE with Maybe'() { 9 | expect: 'having a null reference' 10 | // tag::maybeBasic1[] 11 | assert 1 == Fnz.Maybe(null).or(Fnz.Just(0)).get() + 1 12 | // end::maybeBasic1[] 13 | } 14 | 15 | void 'handling NPE with Maybe without import'() { 16 | expect: 'having a null reference' 17 | // tag::maybeBasic2[] 18 | assert 1 == Maybe(null).or(Just(0)).get() + 1 19 | // end::maybeBasic2[] 20 | } 21 | 22 | void 'handling NPE with actions'() { 23 | expect: 'having a null reference' 24 | // tag::maybeBasic3[] 25 | assert Maybe(null) 26 | .or(Just(0)) 27 | .fmap { x -> x + 1 } 28 | .fmap { y -> y + 1 } 29 | .get() == 2 30 | // end::maybeBasic3[] 31 | } 32 | 33 | void 'handling NPE with actions and Elvis'() { 34 | expect: 'having a null reference' 35 | // tag::maybeBasic4[] 36 | assert (null ?: 0) 37 | .collect { x -> x + 1 } 38 | .collect { y -> y + 1 } 39 | .find() == 2 40 | // end::maybeBasic4[] 41 | } 42 | 43 | // tag::maybeBasic5[] 44 | void 'invoking a method depending on Maybe'() { 45 | when: 'having a null reference' 46 | def number = Maybe(null) // == Nothing() 47 | def result = execute(number) 48 | 49 | then: 'we should get a bad result' 50 | result == 'bad' 51 | } 52 | 53 | String execute(fnz.data.Maybe.Nothing nothing) { 54 | return 'bad' 55 | } 56 | 57 | String execute(fnz.data.Maybe.Just value) { 58 | return 'good' 59 | } 60 | // end::maybeBasic5[] 61 | 62 | } 63 | -------------------------------------------------------------------------------- /fnz-core/src/test/groovy/fnz/ast/flow/LetmSpec.groovy: -------------------------------------------------------------------------------- 1 | package fnz.ast.flow 2 | 3 | import static fnz.Fnz.val 4 | import static fnz.Fnz.Right 5 | 6 | import fnz.ast.FnzAst 7 | 8 | import fnz.data.Try 9 | import fnz.data.Maybe 10 | import fnz.data.Either 11 | import fnz.test.AstBaseSpec 12 | import org.codehaus.groovy.control.CompilePhase 13 | 14 | class LetmSpec extends AstBaseSpec { 15 | 16 | def exampleInstance 17 | 18 | def setup() { 19 | exampleInstance = 20 | getClassToTestForPhase( 21 | FnzAst, 22 | CompilePhase.CONVERSION).newInstance() 23 | } 24 | 25 | def 'simple letm'() { 26 | when: 'Initializing expression and executing closure' 27 | Maybe result = 28 | exampleInstance.simpleLetmExpression() 29 | then: 'There should be a value' 30 | result.isPresent() 31 | and: 'The value should be built from the deeper values' 32 | val(result) == 30 33 | } 34 | 35 | def 'Nesting lets'() { 36 | when: 'Initializing expression and executing closure' 37 | Maybe result = 38 | exampleInstance.nestingLetms() 39 | then: 'There should be a value' 40 | result.isPresent() 41 | and: 'The value should be built from the deeper values' 42 | val(result) == 43 43 | } 44 | 45 | def 'combine with either monad'() { 46 | when: 'executing' 47 | Either result = 48 | exampleInstance.workingWithOtherMonads(1, 2) 49 | then:'we should get the ' 50 | result instanceof Either.Right 51 | and: 'Result is the expected' 52 | val(result) == 3 53 | } 54 | 55 | def 'combine with try monad'() { 56 | when: 'executing a possible dangerous method' 57 | Try result = exampleInstance.combineWithTry(a, b) 58 | then: 'we should get a failure instance' 59 | result.class.isAssignableFrom(expected) 60 | where: 'possible values and expectations are' 61 | a | b | expected 62 | 1 | 0 | Try.Failure 63 | 1 | 1 | Try.Success 64 | } 65 | 66 | } 67 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/includes/maybe.adoc: -------------------------------------------------------------------------------- 1 | All abstractions are available at `fnz.Fnz` as static functions. 2 | 3 | [source,groovy] 4 | ---- 5 | include::../test/groovy/MaybeExampleSpec.groovy[tags=importStaticTry,indent=0] 6 | ---- 7 | 8 | The following example is really simple, just adding one to a value, only if 9 | the value is null then use zero instead: 10 | 11 | [source,groovy] 12 | ---- 13 | include::../test/groovy/MaybeExampleSpec.groovy[tags=maybeBasic1, indent=0] 14 | ---- 15 | 16 | **IMPORTANT**: If you use Fnz in Groovy code, you don't have to 17 | import the `Fnz` class, `Fnz` has an extension module that exposes 18 | directly all those functions for you. 19 | 20 | [source,groovy] 21 | ---- 22 | include::../test/groovy/MaybeExampleSpec.groovy[tags=maybeBasic2, indent=0] 23 | ---- 24 | 25 | Ok, but... Groovy already has the **Elvis operator**, which is simpler, 26 | and less verbose.. so why should I use this anyway ? 27 | 28 | Well if you're only evaluating a value this may sound overkill, but if 29 | you would like to concatenate a set of operation over a possible 30 | `null` value, then it could be very helpful: 31 | 32 | [source,groovy] 33 | ---- 34 | include::../test/groovy/MaybeExampleSpec.groovy[tags=maybeBasic3, indent=0] 35 | ---- 36 | 37 | And the plain Groovy code: 38 | 39 | [source,groovy] 40 | ---- 41 | include::../test/groovy/MaybeExampleSpec.groovy[tags=maybeBasic4, indent=0] 42 | ---- 43 | 44 | **Wait!** plain Groovy is not that bad, I think I stick to **Elvis**, 45 | Do you have anything else to show ? **Yes I do!** Can you call a 46 | method depending on null ? No right ? And that makes you whether use 47 | a **default parameter** or have another conditional statement. 48 | 49 | Having a type representing the **null** value enables programmer to 50 | override methods depending on whether the value was `Nothing` or 51 | `Just`. It's just not possible to do that with plain Groovy (even 52 | though null is an instance of 53 | org.codehaus.groovy.runtime.NullObject). Lets see this subject: 54 | 55 | [source,groovy] 56 | ---- 57 | include::../test/groovy/MaybeExampleSpec.groovy[tags=maybeBasic5, indent=0] 58 | ---- 59 | 60 | No conditionals required... **Elvis** has left the building ?:) 61 | 62 | -------------------------------------------------------------------------------- /fnz-core/src/main/groovy/fnz/ast/flow/UnlessAstTransformer.groovy: -------------------------------------------------------------------------------- 1 | package fnz.ast.flow 2 | 3 | import static org.codehaus.groovy.ast.ClassHelper.make 4 | import static org.codehaus.groovy.ast.tools.GeneralUtils.notX 5 | import static org.codehaus.groovy.ast.tools.GeneralUtils.ternaryX 6 | import static org.codehaus.groovy.ast.tools.GeneralUtils.callX 7 | 8 | import static fnz.ast.AstUtils.getArgs 9 | import static fnz.ast.AstUtils.callClosureX 10 | import static fnz.ast.AstUtils.getLastArgumentAs 11 | 12 | import org.codehaus.groovy.ast.expr.Expression 13 | import org.codehaus.groovy.ast.expr.ClosureExpression 14 | import org.codehaus.groovy.ast.expr.MethodCallExpression 15 | import org.codehaus.groovy.ast.expr.ArgumentListExpression 16 | 17 | import groovy.transform.CompileStatic 18 | import org.codehaus.groovy.control.SourceUnit 19 | 20 | import fnz.Fnz 21 | import fnz.ast.MethodCallExpressionTransformer 22 | 23 | /** 24 | * This transformer transforms expressions like this: 25 | *
26 |  * unless(something == 2) {
27 |  *    executeSomethingElse()
28 |  * }
29 |  * 
30 | * into this: 31 | *
32 |  * if (!(something == 2)) {
33 |  *    executeSomethingElse()
34 |  * }
35 |  * 
36 | * 37 | * @author Mario Garcia 38 | * 39 | */ 40 | @CompileStatic 41 | class UnlessAstTransformer extends MethodCallExpressionTransformer { 42 | 43 | static final String JUST = 'Just' 44 | static final String NOTHING = 'Nothing' 45 | 46 | UnlessAstTransformer(SourceUnit sourceUnit) { 47 | super(sourceUnit, 'unless') 48 | } 49 | 50 | @Override 51 | Expression transformMethodCall(MethodCallExpression unlessExpression) { 52 | ArgumentListExpression args = getArgs(unlessExpression) 53 | Expression booleanExpression = args.first() 54 | ClosureExpression bodyExpression = getLastArgumentAs(args, ClosureExpression) 55 | 56 | // This introspects closure structure and applies 57 | // transform(...) on all nodes 58 | this.visitClosureExpression(bodyExpression) 59 | 60 | return ternaryX( 61 | notX(booleanExpression), 62 | callX(make(Fnz, false), JUST, callClosureX(bodyExpression)), 63 | callX(make(Fnz, false), NOTHING) 64 | ) 65 | 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /fnz-core/src/main/groovy/fnz/ast/MethodCallExpressionTransformer.groovy: -------------------------------------------------------------------------------- 1 | package fnz.ast 2 | 3 | import org.codehaus.groovy.ast.expr.Expression 4 | import org.codehaus.groovy.ast.expr.MethodCallExpression 5 | 6 | import org.codehaus.groovy.control.SourceUnit 7 | 8 | /** 9 | * This class can be used as a base to transform certain MethodCallExpression instances. 10 | * 11 | * Let's say we had a expression like: 12 | *
inventedmethod(x:1) {}
13 | * We can transform that expression into another creating an instance of a MethodCallExpression: 14 | *
15 |  * class InventedMethodTransformer extends MethodCallExpressionTransformer {
16 |  *       InventedMethodTransformer(SourceUnit sourceUnit) {
17 |  *           super(sourceUnit, 'inventedMethod')
18 |  *       }
19 |  *
20 |  *       Expression transformMethodCall(MethodCallExpression methodCallExpression) {
21 |  *           // implement the transformation
22 |  *       }
23 |  * }
24 |  * 
25 | * @author Mario Garcia 26 | */ 27 | abstract class MethodCallExpressionTransformer extends DefaultClassCodeExpressionTransformer { 28 | 29 | String methodCallName 30 | 31 | /** 32 | * Every instance needs the source unit awareness and the name of the method 33 | * it's going to transform 34 | * 35 | * @param sourceUnit Needed to apply scope 36 | * @param name The name of the method you want to transform 37 | */ 38 | MethodCallExpressionTransformer(SourceUnit sourceUnit, String name) { 39 | super(sourceUnit) 40 | this.methodCallName = name 41 | } 42 | 43 | @Override 44 | Expression transform(Expression expression) { 45 | if (!expression) return 46 | 47 | return isTransformable(expression) ? 48 | transformMethodCall(expression) : 49 | expression.transformExpression(this) 50 | } 51 | 52 | Boolean isTransformable(final Expression expression) { 53 | return expression instanceof MethodCallExpression && 54 | expression.methodAsString == this.methodCallName 55 | } 56 | 57 | /** 58 | * This method will transform the expression into its final version. 59 | * 60 | * @param methodCallExpression the method expression you want to transform 61 | * @return the final version of the method expression 62 | */ 63 | abstract Expression transformMethodCall(MethodCallExpression methodCallExpression) 64 | 65 | } 66 | -------------------------------------------------------------------------------- /fnz-site/src/site/SiteConfig.groovy: -------------------------------------------------------------------------------- 1 | import com.sysgears.theme.ResourceMapper 2 | import com.sysgears.theme.deploy.GHPagesDeployer 3 | import com.sysgears.theme.taglib.ThemeTagLib 4 | 5 | // Resource mapper and tag libs. 6 | resource_mapper = new ResourceMapper(site).map 7 | tag_libs = [ThemeTagLib] 8 | features { 9 | compass = 'none' 10 | highlight = 'pygments' // 'none', 'pygments' 11 | markdown = 'txtmark' // 'txtmark', 'pegdown' 12 | cache_highlight=false 13 | } 14 | 15 | destination_dir= "../../build/site/" 16 | 17 | environments { 18 | dev { 19 | log.info 'Development environment is used' 20 | url = "http://localhost:${jetty_port}" 21 | show_unpublished = true 22 | } 23 | prod { 24 | log.info 'Production environment is used' 25 | url = '' // site URL, for example http://www.example.com 26 | show_unpublished = false 27 | features { 28 | minify_xml = false 29 | minify_html = false 30 | minify_js = false 31 | minify_css = false 32 | } 33 | } 34 | cmd { 35 | features { 36 | compass = 'none' 37 | highlight = 'none' 38 | } 39 | } 40 | } 41 | 42 | python { 43 | interpreter = 'jython' // 'auto', 'python', 'jython' 44 | //cmd_candidates = ['python2', 'python', 'python2.7'] 45 | //setup_tools = '2.1' 46 | } 47 | 48 | ruby { 49 | interpreter = 'jruby' // 'auto', 'ruby', 'jruby' 50 | //cmd_candidates = ['ruby', 'ruby1.8.7', 'ruby1.9.3', 'user.home/.rvm/bin/ruby'] 51 | //ruby_gems = '2.2.2' 52 | } 53 | 54 | // Deployment settings. 55 | s3_bucket = '' // your S3 bucket name 56 | deploy_s3 = "s3cmd sync --acl-public --reduced-redundancy ${destination_dir}/ s3://${s3_bucket}/" 57 | 58 | gh_pages_url = '' // path to GitHub repository in format git@github.com:{username}/{repo}.git 59 | deploy = new GHPagesDeployer(site).deploy 60 | 61 | // Custom commands-line commands. 62 | commands = [ 63 | /* 64 | * Creates new page. 65 | * 66 | * location - relative path to the new page, should start with the /, i.e. /pages/index.html. 67 | * pageTitle - new page title 68 | */ 69 | 'create-page': { String location, String pageTitle -> 70 | file = new File(content_dir, location) 71 | file.parentFile.mkdirs() 72 | file.exists() || file.write("""--- 73 | layout: default 74 | title: "${pageTitle}" 75 | published: true 76 | --- 77 | """)} 78 | ] 79 | -------------------------------------------------------------------------------- /fnz-core/src/main/groovy/fnz/ast/AstUtils.groovy: -------------------------------------------------------------------------------- 1 | package fnz.ast 2 | 3 | import static org.codehaus.groovy.ast.tools.GeneralUtils.callX 4 | 5 | import groovy.transform.CompileStatic 6 | 7 | import org.codehaus.groovy.syntax.Token 8 | import org.codehaus.groovy.control.SourceUnit 9 | 10 | import org.codehaus.groovy.ast.expr.Expression 11 | import org.codehaus.groovy.ast.expr.BinaryExpression 12 | import org.codehaus.groovy.ast.expr.ClosureExpression 13 | import org.codehaus.groovy.ast.expr.ArgumentListExpression 14 | import org.codehaus.groovy.ast.expr.MethodCallExpression 15 | 16 | import org.codehaus.groovy.ast.stmt.Statement 17 | import org.codehaus.groovy.ast.stmt.ExpressionStatement 18 | 19 | import org.codehaus.groovy.classgen.VariableScopeVisitor 20 | 21 | @CompileStatic 22 | final class AstUtils { 23 | 24 | static final String DO_CALL_METHOD_NAME = 'doCall' 25 | 26 | static void applyScopeVisitor(final MethodCallExpression expression, final SourceUnit sourceUnit) { 27 | VariableScopeVisitor variableScopeVisitor = new VariableScopeVisitor(sourceUnit) 28 | variableScopeVisitor.prepareVisit(sourceUnit.AST.scriptClassDummy) 29 | variableScopeVisitor.visitMethodCallExpression(expression) 30 | } 31 | 32 | static Expression callClosureX(final ClosureExpression closure) { 33 | return callX(closure, DO_CALL_METHOD_NAME) 34 | } 35 | 36 | static Expression callClosureX(final ClosureExpression closure, ArgumentListExpression args) { 37 | return callX(closure, DO_CALL_METHOD_NAME, args) 38 | } 39 | 40 | static Boolean isBinaryExpression(final Expression expression) { 41 | return expression instanceof BinaryExpression 42 | } 43 | 44 | static Boolean isClosureExpression(final Expression expression) { 45 | return expression instanceof ClosureExpression 46 | } 47 | 48 | static Boolean isExpressionStatement(final Statement statement) { 49 | return statement instanceof ExpressionStatement 50 | } 51 | 52 | static ArgumentListExpression getArgs(final MethodCallExpression methodCallExpression) { 53 | return (ArgumentListExpression) methodCallExpression.arguments 54 | } 55 | 56 | static U getFirstArgumentAs(final ArgumentListExpression args, Class asType) { 57 | return asType.cast(args.expressions.first()) 58 | } 59 | 60 | static Boolean isToken(Token token, int typeRef) { 61 | return token.type == typeRef 62 | } 63 | 64 | static U getLastArgumentAs(final ArgumentListExpression args, Class asType) { 65 | return asType.cast(args.expressions.last()) 66 | } 67 | 68 | static String getUniqueIdentifier() { 69 | return "_${System.nanoTime()}" 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /fnz-core/src/main/groovy/fnz/ast/DefaultClassCodeExpressionTransformer.groovy: -------------------------------------------------------------------------------- 1 | package fnz.ast 2 | 3 | import groovyjarjarasm.asm.Opcodes 4 | import org.codehaus.groovy.control.SourceUnit 5 | import org.codehaus.groovy.ast.ASTNode 6 | import org.codehaus.groovy.ast.ModuleNode 7 | import org.codehaus.groovy.ast.ClassCodeExpressionTransformer 8 | import org.codehaus.groovy.ast.expr.Expression 9 | import org.codehaus.groovy.syntax.SyntaxException 10 | 11 | /** 12 | * Most transformers need at some point the source unit in order to fix or apply 13 | * properly the scope to each variable. 14 | * 15 | * This class enforces the use of a SourceUnit instance for every transformer 16 | * 17 | * @author Mario Garcia 18 | * 19 | */ 20 | abstract class DefaultClassCodeExpressionTransformer 21 | extends ClassCodeExpressionTransformer implements Opcodes { 22 | 23 | private SourceUnit sourceUnit 24 | 25 | /** 26 | * This constructor needs a source unit 27 | * 28 | * @param sourceUnit the related source unit where the expression belongs 29 | */ 30 | DefaultClassCodeExpressionTransformer(SourceUnit sourceUnit) { 31 | this.sourceUnit = sourceUnit 32 | } 33 | 34 | /** 35 | * This method returns the source unit 36 | * 37 | * @return the source unit related to the expression we want to transform 38 | */ 39 | SourceUnit getSourceUnit() { 40 | return this.sourceUnit 41 | } 42 | 43 | /** 44 | * This method creates a new SyntaxException 45 | * 46 | * @param node The node causing the exception 47 | * @param message A meaningful exception message to the user 48 | */ 49 | void addError(ASTNode node, String message) { 50 | sourceUnit.addError( 51 | new SyntaxException( 52 | message, 53 | node.columnNumber, 54 | node.lineNumber 55 | ) 56 | ) 57 | } 58 | 59 | /** 60 | * This method returns the module of the current 61 | * SourceUnit instance 62 | * 63 | * @return a ModuleNode instance 64 | */ 65 | ModuleNode getModule() { 66 | return sourceUnit.AST 67 | } 68 | 69 | /** 70 | * Sometimes could be useful to get the package name 71 | * of the current module 72 | * 73 | * @return A String representing the current qualified package name 74 | */ 75 | String getModulePackageName() { 76 | return module?.packageName?.with { "$it" } ?: '' 77 | } 78 | 79 | /** 80 | * Classes implementing this method should provide a way 81 | * of knowing whether the expression should be transform 82 | * or not 83 | * 84 | * @param expression the expression we may want to transform 85 | * @return true if the current expression should be transformed or 86 | * false otherwise 87 | */ 88 | abstract Boolean isTransformable(Expression expression) 89 | 90 | } 91 | -------------------------------------------------------------------------------- /fnz-core/src/main/java/fnz/Fnz.java: -------------------------------------------------------------------------------- 1 | package fnz; 2 | 3 | import fnz.data.Try; 4 | import fnz.data.Monad; 5 | import fnz.data.Either; 6 | import fnz.data.Maybe; 7 | import fnz.data.Functor; 8 | import fnz.data.Function; 9 | import fnz.data.TypeAwareFunction; 10 | import fnz.data.ListMonad; 11 | import fnz.data.Applicative; 12 | 13 | /** 14 | * 15 | */ 16 | public final class Fnz { 17 | 18 | public static
Either.Left Left(A source) { 19 | return Either.left(source); 20 | } 21 | 22 | public static Either.Right Right(A source) { 23 | return Either.right(source); 24 | } 25 | 26 | public static Either Either(A source) { 27 | return (Either) (source != null ? Right(source) : Left(source)); 28 | } 29 | 30 | public static Maybe.Just Just(A source) { 31 | return Maybe.just(source); 32 | } 33 | 34 | public static Maybe.Nothing Nothing() { 35 | return Maybe.nothing(); 36 | } 37 | 38 | public static Maybe Maybe(A source) { 39 | return Maybe.maybe(source); 40 | } 41 | 42 | public static > Maybe Maybe(M source) { 43 | return Maybe.maybe(source); 44 | } 45 | 46 | public static Try.Success Success(A source) { 47 | return Try.success(source); 48 | } 49 | 50 | public static Try.Failure Failure() { 51 | return Try.failure(new NullPointerException()); 52 | } 53 | 54 | public static Try.Failure Failure(A a) { 55 | return Try.failure(a); 56 | } 57 | 58 | public static ListMonad List(A... values) { 59 | return ListMonad.list(values); 60 | } 61 | 62 | public static ListMonad List(Iterable values) { 63 | return ListMonad.list(values); 64 | } 65 | 66 | public static Try Try(Function fn) { 67 | return Try.Try(fn); 68 | } 69 | 70 | public static Try Try(A a, Function fn) { 71 | return Try.Try(a, fn); 72 | } 73 | 74 | public static > Function> wrap(F fn) { 75 | return Try.wrap(fn); 76 | } 77 | 78 | public static Function> recover(Function... alternatives) { 79 | return Try.recover(alternatives); 80 | } 81 | 82 | public static ,MB extends Monad> MB bind(MA ma, Function fn) { 83 | return ma.bind(fn); 84 | } 85 | 86 | public static , AB extends Applicative> AB fapply(AA fa, Applicative> fn) { 87 | return (AB) fa.fapply(fn); 88 | } 89 | 90 | // tag::fmap[] 91 | public static , FB extends Functor> FB fmap(FA fa, Function fn) { 92 | return fa.fmap(fn); 93 | } 94 | // end::fmap[] 95 | 96 | public static A val(Monad monad) { 97 | return monad != null ? monad.getTypedRef().getValue() : null; 98 | } 99 | 100 | public static A get(Monad monad) { 101 | return monad.get(); 102 | } 103 | 104 | public static A get(A value) { 105 | return value; 106 | } 107 | 108 | } 109 | -------------------------------------------------------------------------------- /fnz-core/src/test/groovy/fnz/data/FunctorSpec.groovy: -------------------------------------------------------------------------------- 1 | package fnz.data 2 | 3 | // tag::publicapimethods[] 4 | import static fnz.Fnz.val 5 | import static fnz.Fnz.fmap 6 | // end::publicapimethods[] 7 | 8 | import spock.lang.Specification 9 | 10 | /** 11 | * 12 | */ 13 | class FunctorSpec extends Specification { 14 | 15 | // tag::functorspec1[] 16 | void 'applying a given function to a functor'() { 17 | given: 'a function' 18 | Function plus_3 = { Integer v -> v + 3 } 19 | and: 'a functor implementation. This time Maybe.Just implements functor' 20 | Functor boxOfFive = Maybe.just(5) 21 | when: 'applying the function to functor to get another functor' 22 | Functor result = boxOfFive.fmap(plus_3) 23 | then: 'the result should be the expected' 24 | result.typedRef.value == 8 25 | } 26 | // end::functorspec1[] 27 | 28 | // tag::functorspec3[] 29 | void 'applying a given function to a functor with public api'() { 30 | when: 'using fmap function to execute the fmap method from Just' 31 | Functor result = 32 | fmap(Maybe.just(5)) { Integer x -> 33 | x + 3 34 | } 35 | then: 'executing val() to get Just inner value' 36 | val(result) == 8 37 | } 38 | // end::functorspec3[] 39 | 40 | // tag::groovyfunctormockery[] 41 | def map(Object o, Closure function) { 42 | o?.with(function) 43 | } 44 | // end::groovyfunctormockery[] 45 | 46 | // tag::groovyfunctor[] 47 | void 'Groovy light version of fmap'() { 48 | when: 'using fmap function to execute the fmap method from Just' 49 | def result = map(input) { Integer x -> 50 | map(x + 3) { Integer y -> 51 | y 52 | } 53 | } 54 | then: 'executing val() to get Just inner value' 55 | result == expected // 8 and null 56 | where: 57 | input | expected 58 | 5 | 8 59 | null | null 60 | } 61 | // end::groovyfunctor[] 62 | 63 | // tag::collect[] 64 | void 'Groovy collect is "similar" to fmap'() { 65 | when: 'Applying a function to collect' 66 | def result = 67 | input.collect { Integer x -> 68 | x + 1 69 | } 70 | then: 'checking' 71 | result == expected 72 | where: 'possible messy values' 73 | input | expected 74 | [1, 2] | [2, 3] 75 | null | [] 76 | } 77 | // end::collect[] 78 | 79 | // tag::collectandmap[] 80 | void 'Groovy light version of fmap and collect combined'() { 81 | when: 'Making the world a safer place' 82 | def result = 83 | input.collect { Integer x -> 84 | map(x) { Integer y -> 85 | (y + 1) * 2 86 | } 87 | } 88 | then: 'checking' 89 | result == expected 90 | where: 'possible messy values are' 91 | input | expected 92 | [1, 2] | [4, 6] 93 | [1, null] | [4, null] 94 | } 95 | // end::collectandmap[] 96 | 97 | } 98 | -------------------------------------------------------------------------------- /fnz-site/build.gradle: -------------------------------------------------------------------------------- 1 | // _____ _ _ 2 | // | __ \| | (_) 3 | // | |__) | |_ _ __ _ _ _ __ ___ 4 | // | ___/| | | | |/ _` | | '_ \/ __| 5 | // | | | | |_| | (_| | | | | \__ \ 6 | // |_| |_|\__,_|\__, |_|_| |_|___/ 7 | // __/ | 8 | // |___/ 9 | 10 | // tag::buildScript[] 11 | buildscript { 12 | repositories { 13 | jcenter() 14 | } 15 | dependencies { 16 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0' 17 | classpath 'com.sysgears.grain:grain-gradle-plugin:0.2.0' 18 | classpath 'org.asciidoctor:asciidoctor-gradle-plugin:1.5.2' 19 | } 20 | } 21 | // end::buildScript[] 22 | 23 | apply plugin: 'groovy' 24 | apply plugin: 'grain' 25 | apply plugin: 'org.asciidoctor.convert' 26 | 27 | // _____ _ _ 28 | // | __ \ | | (_) 29 | // | | | | ___ _ __ ___ _ __ __| | ___ _ __ ___ _ ___ ___ 30 | // | | | |/ _ \ '_ \ / _ \ '_ \ / _` |/ _ \ '_ \ / __| |/ _ \/ __| 31 | // | |__| | __/ |_) | __/ | | | (_| | __/ | | | (__| | __/\__ \ 32 | // |_____/ \___| .__/ \___|_| |_|\__,_|\___|_| |_|\___|_|\___||___/ 33 | // | | 34 | // |_| 35 | 36 | repositories { 37 | jcenter() 38 | } 39 | 40 | dependencies { 41 | compile project(':fnz-core') 42 | 43 | compile 'org.codehaus.groovy:groovy-all:2.4.0' 44 | compile 'org.slf4j:slf4j-api:1.7.5' 45 | testCompile 'junit:junit:4.11' 46 | testCompile 'org.spockframework:spock-core:1.0-groovy-2.4' 47 | } 48 | 49 | // _____ __ _ _ _ 50 | // / ____| / _(_) | | (_) 51 | // | | ___ _ __ | |_ _ __ _ _ _ _ __ __ _| |_ _ ___ _ __ 52 | // | | / _ \| '_ \| _| |/ _` | | | | '__/ _` | __| |/ _ \| '_ \ 53 | // | |___| (_) | | | | | | | (_| | |_| | | | (_| | |_| | (_) | | | | 54 | // \_____\___/|_| |_|_| |_|\__, |\__,_|_| \__,_|\__|_|\___/|_| |_| 55 | // __/ | 56 | // |___/ 57 | 58 | asciidoctor { 59 | sourceDir 'src/docs' 60 | attributes( 61 | 'icons': 'font', 62 | 'source-highlighter': 'prettify', 63 | 'toc': 'left' 64 | ) 65 | } 66 | 67 | // _____ _ _ _ _ _ _ 68 | // | __ \(_) | | (_) | | | (_) 69 | // | | | |_ ___| |_ _ __ _| |__ _ _| |_ _ ___ _ __ 70 | // | | | | / __| __| '__| | '_ \| | | | __| |/ _ \| '_ \ 71 | // | |__| | \__ \ |_| | | | |_) | |_| | |_| | (_) | | | | 72 | // |_____/|_|___/\__|_| |_|_.__/ \__,_|\__|_|\___/|_| |_| 73 | // 74 | 75 | group = 'fnz-site' 76 | version = '1.0.6' 77 | 78 | // _____ _ _ _ 79 | // / ____| | | | | | | 80 | // | | _ _ ___| |_ ___ _ __ ___ | |_ __ _ ___| | _____ 81 | // | | | | | / __| __/ _ \| '_ ` _ \ | __/ _` / __| |/ / __| 82 | // | |___| |_| \__ \ || (_) | | | | | | | || (_| \__ \ <\__ \ 83 | // \_____\__,_|___/\__\___/|_| |_| |_| \__\__,_|___/_|\_\___/ 84 | // 85 | 86 | task('docs') << { 87 | group = 'fnz-site' 88 | description 'Moves asciidoctor docs to Grain site folders' 89 | 90 | file("build/asciidoc/html5/").renameTo(file("build/site/docs/")) 91 | delete(file("build/asciidoc/")) 92 | } 93 | 94 | docs.dependsOn asciidoctor 95 | asciidoctor.dependsOn grainGenerate 96 | -------------------------------------------------------------------------------- /fnz-core/src/main/groovy/fnz/flow/Where.groovy: -------------------------------------------------------------------------------- 1 | package fnz.flow 2 | 3 | import static org.codehaus.groovy.runtime.DefaultGroovyMethods.isCase 4 | import static fnz.Fnz.Just 5 | 6 | import groovy.transform.CompileStatic 7 | 8 | import fnz.data.Maybe 9 | 10 | /** 11 | * This control was inspired in Haskell's where clause. 12 | * 13 | * @author Mario Garcia 14 | * @since 0.1 15 | */ 16 | @CompileStatic 17 | class Where { 18 | 19 | private static class Evaluation { 20 | Closure condition 21 | Closure execution 22 | } 23 | 24 | private List conditions = [] 25 | private Map parameters = [:] 26 | 27 | private Where(final Map parameters) { 28 | this.parameters = parameters 29 | } 30 | 31 | Where when(final Closure cl) { 32 | conditions << new Evaluation(condition:cl) 33 | return this 34 | } 35 | 36 | @SuppressWarnings('SpaceAroundMapEntryColon') 37 | Where when(final Class clazz) { 38 | conditions << new Evaluation(condition: { isCase(clazz, parameters.val) }) 39 | return this 40 | } 41 | 42 | @SuppressWarnings('SpaceAroundMapEntryColon') 43 | Where when(final Collection collection) { 44 | conditions << new Evaluation(condition: { isCase(collection, parameters.val) }) 45 | return this 46 | } 47 | 48 | @SuppressWarnings('SpaceAroundMapEntryColon') 49 | Where when(final Number number) { 50 | conditions << new Evaluation(condition: { isCase(number, parameters.val) }) 51 | return this 52 | } 53 | 54 | Where then(final Closure cl) { 55 | conditions.last().execution = cl 56 | return this 57 | } 58 | 59 | @SuppressWarnings('SpaceAroundMapEntryColon') 60 | Where otherwise(final Closure cl) { 61 | conditions << new Evaluation(condition: { true }, execution:cl) 62 | return this 63 | } 64 | 65 | Where where (final Closure whereClause) { 66 | this.with(whereClause) 67 | return this 68 | } 69 | 70 | def propertyMissing(String name, value) { 71 | parameters.get(name, value) 72 | } 73 | 74 | def evaluate() { 75 | Closure byApplyingParameters = applyDelegateToExecution.call(parameters) 76 | Closure execution = 77 | conditions. 78 | collect(byApplyingParameters). 79 | find(firstTrue). 80 | execution 81 | 82 | return execution.call() 83 | } 84 | 85 | private Closure> applyDelegateToExecution = { final parameters -> 86 | return { final Evaluation evaluation -> 87 | evaluation.execution.delegate = parameters 88 | evaluation.condition.delegate = parameters 89 | evaluation 90 | } 91 | } 92 | 93 | private Closure firstTrue = { Evaluation evaluation -> 94 | evaluation.condition.call() 95 | } 96 | 97 | public static Maybe check(final Map values, final Closure evaluation) { 98 | return internalCheck(values ?: [:], evaluation) 99 | } 100 | 101 | public static Maybe check(final Object value, final Closure evaluation) { 102 | return internalCheck([val:value], evaluation) 103 | } 104 | 105 | private static Maybe internalCheck(final Object value, final Closure evaluation) { 106 | def where = new Where((Map) value) 107 | where.with(evaluation) 108 | return Just(where.evaluate()) 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /fnz-core/src/main/groovy/fnz/ast/FnzExtensionModule.groovy: -------------------------------------------------------------------------------- 1 | package fnz.ast 2 | 3 | import groovy.transform.CompileStatic 4 | 5 | import fnz.Fnz 6 | import fnz.data.Try 7 | import fnz.data.Monad 8 | import fnz.data.Either 9 | import fnz.data.Maybe 10 | import fnz.data.Functor 11 | import fnz.data.Function 12 | import fnz.data.Applicative 13 | import fnz.data.ListMonad 14 | import fnz.data.TypeAwareFunction 15 | 16 | @CompileStatic 17 | final class FnzExtensionModule { 18 | 19 | static Either.Left Left(Object o, A source) { 20 | return Fnz.Left(source) 21 | } 22 | 23 | static Either.Right Right(Object o, A source) { 24 | return Fnz.Right(source) 25 | } 26 | 27 | static Either Either(Object o, A source) { 28 | return Fnz.Either(source) 29 | } 30 | 31 | static Maybe.Just Just(Object o, A source) { 32 | return Fnz.Just(source) 33 | } 34 | 35 | static Maybe.Nothing Nothing(Object o) { 36 | return Fnz.Nothing() 37 | } 38 | 39 | static Maybe Maybe(Object o, A source) { 40 | return Fnz.Maybe(source) 41 | } 42 | 43 | static > Maybe Maybe(Object o, M source) { 44 | return Fnz.Maybe(source) 45 | } 46 | 47 | static ListMonad List(Object o, A... values) { 48 | return Fnz.List(values) 49 | } 50 | 51 | static ListMonad List(Object o, Iterable values) { 52 | return Fnz.List(values) 53 | } 54 | 55 | static Try.Success Success(Object o, A source) { 56 | return Fnz.Success(source) 57 | } 58 | 59 | static Try.Failure Failure(Object o) { 60 | return Fnz.Failure() 61 | } 62 | 63 | static Try.Failure Failure(Object o, A a) { 64 | return Fnz.Failure(a) 65 | } 66 | 67 | public static Try Try(Object o, Function fn) { 68 | return Fnz.Try(fn); 69 | } 70 | 71 | public static Try Try(Object o, A a, Function fn) { 72 | return Fnz.Try(a, fn); 73 | } 74 | 75 | static > Function> wrap(Object o, F fn) { 76 | return Fnz.wrap(fn) 77 | } 78 | 79 | static ,MB extends Monad> MB bind(Object o, MA ma, Function fn) { 80 | return Fnz.bind(ma, fn) 81 | } 82 | 83 | static , AB extends Applicative> AB fapply( 84 | Object o, AA fa, Applicative> fn) { 85 | return Fnz.fapply(fa, fn) 86 | } 87 | 88 | static , FB extends Functor> FB fmap(Object o, FA fa, Function fn) { 89 | return Fnz.fmap(fa, fn) 90 | } 91 | 92 | static A val(Object o, Monad monad) { 93 | return Fnz.val(monad) 94 | } 95 | 96 | static Function> recover(Object o, Closure... alternatives) { 97 | return Fnz.recover(alternatives as Function[]); 98 | } 99 | 100 | static > M bind(Iterable col, Function fn) { 101 | return col instanceof ListMonad ? col.bind(fn) : Fnz.List(col).bind(fn) 102 | } 103 | 104 | static > M bind2(Iterable col, TypeAwareFunction fn) { 105 | return col instanceof ListMonad ? col.bind2(fn) : Fnz.List(col).bind2(fn) 106 | } 107 | 108 | static Applicative fapply(Iterable col, Applicative> afn) { 109 | return col instanceof ListMonad ? col.fapply(afn) : Fnz.List(col).fapply(afn) 110 | } 111 | 112 | static > F fmap(Iterable col, Function fn) { 113 | return col instanceof ListMonad ? col.fmap(fn) : Fnz.List(col).fmap(fn) 114 | } 115 | 116 | } 117 | -------------------------------------------------------------------------------- /fnz-core/src/main/java/fnz/data/Either.java: -------------------------------------------------------------------------------- 1 | package fnz.data; 2 | 3 | /** 4 | * 5 | * @param The contained type 6 | */ 7 | public abstract class Either extends MonadType implements Or> { 8 | 9 | protected Either(Type value) { 10 | super(value); 11 | } 12 | 13 | public abstract boolean isLeft(); 14 | public abstract boolean isRight(); 15 | 16 | public static class Right extends Either { 17 | 18 | private Right(Type valueRef) { 19 | super(valueRef); 20 | } 21 | 22 | @Override 23 | public boolean isLeft() { 24 | return false; 25 | } 26 | 27 | @Override 28 | public boolean isRight() { 29 | return true; 30 | } 31 | 32 | @Override 33 | public Right fapply(Applicative> afn) { 34 | return this.fmap(afn.getTypedRef().getValue()); 35 | } 36 | 37 | @Override 38 | public > M bind(Function fn) { 39 | return fn.apply(getTypedRef().getValue()); 40 | } 41 | 42 | @Override 43 | public > M bind2(TypeAwareFunction fn) { 44 | return fn.apply((Class) this.getClass(), getTypedRef().getValue()); 45 | } 46 | 47 | @Override 48 | public > F fmap(Function fn) { 49 | return (F) right(fn.apply(getTypedRef().getValue())); 50 | } 51 | 52 | public static Right unit(A value) { 53 | return Either.right(value); 54 | } 55 | 56 | @Override 57 | public Either or(Either newOption) { 58 | return this; 59 | } 60 | 61 | @Override 62 | public Either or(Function> newOption) { 63 | return this; 64 | } 65 | 66 | @Override 67 | public Boolean asBoolean() { 68 | return Boolean.TRUE; 69 | } 70 | 71 | } 72 | 73 | public static class Left extends Either { 74 | 75 | private Left(Type valueRef) { 76 | super(valueRef); 77 | } 78 | 79 | @Override 80 | public boolean isLeft() { 81 | return true; 82 | } 83 | 84 | @Override 85 | public boolean isRight() { 86 | return false; 87 | } 88 | 89 | @Override 90 | public Left fapply(Applicative> afn) { 91 | return new Left(getTypedRef()); 92 | } 93 | 94 | @Override 95 | public > M bind(Function fn) { 96 | return (M) new Left(getTypedRef()); 97 | } 98 | 99 | @Override 100 | public > M bind2(TypeAwareFunction fn) { 101 | return (M) new Left(getTypedRef()); 102 | } 103 | 104 | @Override 105 | public > F fmap(Function fn) { 106 | return (F) new Left(getTypedRef()); 107 | } 108 | 109 | @Override 110 | public Either or(Either newOption) { 111 | return newOption; 112 | } 113 | 114 | @Override 115 | public Either or(Function> newOption) { 116 | return newOption.apply(this.getTypedRef().getValue()); 117 | } 118 | 119 | @Override 120 | public Boolean asBoolean() { 121 | return Boolean.FALSE; 122 | } 123 | 124 | } 125 | 126 | public static Right right(T value) { 127 | return new Right(new Type(value)); 128 | } 129 | 130 | public static Left left(T value) { 131 | return new Left(new Type(value)); 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /fnz-core/src/main/java/fnz/data/Maybe.java: -------------------------------------------------------------------------------- 1 | package fnz.data; 2 | 3 | import org.codehaus.groovy.runtime.DefaultGroovyMethods; 4 | 5 | /** 6 | * 7 | * @param The contained type 8 | */ 9 | public abstract class Maybe extends MonadType implements Or> { 10 | 11 | public abstract boolean isPresent(); 12 | 13 | public Maybe(Type valueRef) { 14 | super(valueRef); 15 | } 16 | 17 | public static class Just extends Maybe { 18 | 19 | private Just(Type valueRef) { 20 | super(valueRef); 21 | } 22 | 23 | // tag::fapply[] 24 | @Override 25 | public Just fapply(Applicative> afn) { 26 | return this.fmap(afn.getTypedRef().getValue()); 27 | } 28 | // end::fapply[] 29 | 30 | // tag::functorspec2[] 31 | @Override 32 | public > F fmap(Function fn) { 33 | return (F) just(fn.apply(this.getTypedRef().getValue())); 34 | } 35 | // end::functorspec2[] 36 | 37 | // tag::justbind[] 38 | @Override 39 | public > M bind(Function fn) { 40 | return fn.apply(getTypedRef().getValue()); 41 | } 42 | // end::justbind[] 43 | 44 | @Override 45 | public > M bind2(TypeAwareFunction fn) { 46 | return fn.apply((Class) this.getClass(), getTypedRef().getValue()); 47 | } 48 | 49 | @Override 50 | public boolean isPresent() { 51 | return true; 52 | } 53 | 54 | @Override 55 | public Maybe or(Maybe newOption) { 56 | return this; 57 | } 58 | 59 | @Override 60 | public Maybe or(Function> newOption) { 61 | return this; 62 | } 63 | 64 | public static Just unit(A value) { 65 | return Maybe.just(value); 66 | } 67 | 68 | @Override 69 | public Boolean asBoolean() { 70 | return Boolean.TRUE; 71 | } 72 | 73 | } 74 | 75 | public static class Nothing extends Maybe { 76 | 77 | private Nothing() { 78 | super(new Type(null)); 79 | } 80 | 81 | @Override 82 | public Nothing fapply(Applicative> afn) { 83 | return new Nothing(); 84 | } 85 | 86 | 87 | @Override 88 | public > M bind(Function fn) { 89 | return (M) new Nothing(); 90 | } 91 | 92 | @Override 93 | public > M bind2(TypeAwareFunction fn) { 94 | return (M) new Nothing(); 95 | } 96 | 97 | // tag::nothingbind[] 98 | @Override 99 | public > F fmap(Function fn) { 100 | return (F) new Nothing(); 101 | } 102 | // end::nothingbind[] 103 | 104 | @Override 105 | public boolean isPresent() { 106 | return false; 107 | } 108 | 109 | @Override 110 | public Maybe or(Maybe newOption) { 111 | return newOption; 112 | } 113 | 114 | @Override 115 | public Maybe or(Function> newOption) { 116 | return newOption.apply(this.getTypedRef().getValue()); 117 | } 118 | 119 | @Override 120 | public Boolean asBoolean() { 121 | return Boolean.FALSE; 122 | } 123 | 124 | } 125 | 126 | public static Maybe.Just just(T value) { 127 | return new Maybe.Just(new Type(value)); 128 | } 129 | 130 | public static Maybe.Nothing nothing() { 131 | return new Maybe.Nothing(); 132 | } 133 | 134 | public static Maybe maybe(final T value) { 135 | return (Maybe) (DefaultGroovyMethods.asBoolean(DefaultGroovyMethods.collect(value)) ? just(value) : nothing()); 136 | } 137 | 138 | public static > Maybe maybe(M monad) { 139 | return maybe(monad.getTypedRef().getValue()); 140 | } 141 | 142 | } 143 | -------------------------------------------------------------------------------- /fnz-core/src/main/java/fnz/data/ListMonad.java: -------------------------------------------------------------------------------- 1 | package fnz.data; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.List; 6 | import java.util.Iterator; 7 | 8 | import org.codehaus.groovy.runtime.DefaultGroovyMethods; 9 | 10 | /** 11 | * 12 | * @param The contained type 13 | */ 14 | public class ListMonad implements Monad, Or>, Iterable, Truth { 15 | 16 | private final Iterable value; 17 | 18 | private ListMonad(Iterable values) { 19 | this.value = values; 20 | } 21 | 22 | @Override 23 | public > M bind(Function fn) { 24 | if (isNullOrEmpty()) 25 | return (M) this; 26 | 27 | List items = new ArrayList<>(); 28 | for (A a : this.value) { 29 | ListMonad transformed = (ListMonad) fn.apply(a); 30 | for (B aa : transformed.getTypedRef().getValue()) { 31 | items.add(aa); 32 | } 33 | } 34 | return (M) list(items); 35 | } 36 | 37 | @Override 38 | public > M bind2(TypeAwareFunction fn) { 39 | if (isNullOrEmpty()) 40 | return (M) this; 41 | 42 | List items = new ArrayList<>(); 43 | for (A a : this.value) { 44 | ListMonad transformed = (ListMonad) fn.apply((Class) this.getClass(), a); 45 | for (B aa : transformed.getTypedRef().getValue()) { 46 | items.add(aa); 47 | } 48 | } 49 | return (M) list(items); 50 | } 51 | 52 | @Override 53 | public TypeIterable getTypedRef() { 54 | return new TypeIterable(this.value); 55 | } 56 | 57 | @Override 58 | public Applicative fapply(Applicative> afn) { 59 | if (isNullOrEmpty()) 60 | return (Applicative) this; 61 | return this.fmap(afn.getTypedRef().getValue()); 62 | } 63 | 64 | @Override 65 | public > F fmap(Function fn) { 66 | if (isNullOrEmpty()) 67 | return (F) this; 68 | List transformed = new ArrayList<>(); 69 | for (A v : this.getTypedRef().getValue()) { 70 | transformed.add(fn.apply(v)); 71 | } 72 | return (F) new ListMonad<>(transformed); 73 | } 74 | 75 | @Override 76 | public String toString() { 77 | return !isNullOrEmpty() ? this.getTypedRef().getValue().toString() : Arrays.asList().toString(); 78 | } 79 | 80 | public boolean isNullOrEmpty() { 81 | return this.value == null || !this.value.iterator().hasNext(); 82 | } 83 | 84 | public static ListMonad list(T... values){ 85 | return list(Arrays.asList(values)); 86 | } 87 | 88 | public static ListMonad unit(A value) { 89 | return list(value); 90 | } 91 | 92 | public static ListMonad list(Iterable values){ 93 | return new ListMonad<>(values); 94 | } 95 | 96 | public ListMonad or(ListMonad alternative) { 97 | return isNullOrEmpty() ? alternative : this; 98 | } 99 | 100 | public ListMonad or(Function> alternative) { 101 | return isNullOrEmpty() ? alternative.apply(null) : this; 102 | } 103 | 104 | @Override 105 | public U get() { 106 | return (U) this.getTypedRef().getValue(); 107 | } 108 | 109 | @Override 110 | public Iterator iterator() { 111 | return this.getTypedRef().getValue().iterator(); 112 | } 113 | 114 | @Override 115 | public Boolean asBoolean() { 116 | return this.getTypedRef().getValue().iterator().hasNext(); 117 | } 118 | 119 | public ListMonad filter() { 120 | if (isNullOrEmpty()) { 121 | return this; 122 | } 123 | List transformed = new ArrayList<>(); 124 | for (A v : this.getTypedRef().getValue()) { 125 | if (v instanceof Truth && ((Truth)v).asBoolean()) { 126 | transformed.add(v); 127 | } else if (!(v instanceof Truth) && DefaultGroovyMethods.asBoolean(v)) { 128 | transformed.add(v); 129 | } 130 | } 131 | return new ListMonad<>(transformed); 132 | } 133 | 134 | } 135 | -------------------------------------------------------------------------------- /fnz-site/src/docs/type.ad: -------------------------------------------------------------------------------- 1 | == Types 2 | 3 | This chapter has to do with other ways of creating/using/extending types with Groovy. 4 | 5 | === func 6 | 7 | [quote, Java(TM) spec] 8 | "" 9 | A functional interface is an interface that has just one abstract method (aside from the methods of Object), and thus represents a single function contract 10 | "" 11 | 12 | *func* allows you to declare and create a http://docs.oracle.com/javase/specs/jls/se8/html/jls-9.html#jls-9.8[*functional interface*] 13 | in one line without the verbosity of having to write a full Java interface. 14 | 15 | Normally if you wanted to create a *functional interface* and use it you should be doing something 16 | like this: 17 | 18 | [source, groovy] 19 | ---- 20 | class A { 21 | 22 | interface Fx { 23 | Integer apply(String word) 24 | } 25 | 26 | Integer applyFunction(String word, Fx function) { 27 | return function.apply(word) 28 | } 29 | 30 | } 31 | ---- 32 | 33 | With *func* you can create in one line the same interface: 34 | 35 | [source, groovy] 36 | ---- 37 | include::{testDir}/fnz/ast/type/FuncSpec.groovy[tags=simpleTypeAlias,indent=0] 38 | ---- 39 | 40 | In general the use cases are: 41 | 42 | ==== Simple types 43 | 44 | [source, groovy] 45 | ---- 46 | class OuterClass { 47 | static { 48 | func InterfaceType >= ParameterType >> ReturnType 49 | } 50 | } 51 | ---- 52 | 53 | Of course you can declare as many interfaces as you want: 54 | 55 | [source, groovy] 56 | ---- 57 | class A { 58 | static { 59 | func Fa >= String >> Integer 60 | func Fb >= String >> String 61 | func Fb >= String >> Date 62 | } 63 | } 64 | ---- 65 | 66 | ==== Using generics 67 | 68 | You can also use generics: 69 | 70 | [source, groovy] 71 | ---- 72 | class OuterClass { 73 | static { 74 | func InterfaceType(A,B) >= A >> B 75 | } 76 | } 77 | ---- 78 | 79 | You first should to declare the name of the type and maybe generic 80 | types used in the `Functional Inteface` signature as a parameter or as 81 | return type. 82 | 83 | [source,groovy] 84 | ---- 85 | InterfaceType(A,B) 86 | ---- 87 | 88 | And then you should declare the signature of the function: 89 | 90 | [source,groovy] 91 | ---- 92 | A >> B 93 | ---- 94 | 95 | In this case it means that we will be having a method like this: 96 | 97 | [source,groovy] 98 | ---- 99 | B apply(A a) 100 | ---- 101 | 102 | So as a whole it will be the same as writing: 103 | 104 | [source,groovy] 105 | ---- 106 | interface InterfaceType { 107 | public B apply(A a) 108 | } 109 | ---- 110 | 111 | The parameters and the return type can both use the generic types 112 | declared by the interface type: 113 | 114 | [source, groovy] 115 | ---- 116 | class OuterClass { 117 | static { 118 | InterfaceType(A,B) >= ParameterType(A) >> ReturnType(B) 119 | } 120 | } 121 | ---- 122 | 123 | The following example declares some generic type used at return type: 124 | 125 | [source, groovy] 126 | ---- 127 | include::{testDir}/fnz/ast/type/FuncSpec.groovy[tags=genericsBasicReturnType,indent=0] 128 | ---- 129 | 130 | Now the example uses generics both as parameter and return type 131 | 132 | [source, groovy] 133 | ---- 134 | include::{testDir}/fnz/ast/type/FuncSpec.groovy[tags=fullGenericsExample,indent=0] 135 | ---- 136 | 137 | And mixing generics with other types: 138 | 139 | [source, groovy] 140 | ---- 141 | include::{testDir}/fnz/ast/type/FuncSpec.groovy[tags=fullGenericsWithType,indent=0] 142 | ---- 143 | 144 | ==== Multiple parameters 145 | 146 | So far we've been showing functions with just one parameter or at most 147 | a parameter making use of generics, but wouldn't be great to have a 148 | way of declaring a function with two or more arguments ? 149 | 150 | It turns out you can. The syntax to acomplish this is to use the 151 | Groovy syntax for lists. 152 | 153 | [source, groovy] 154 | ---- 155 | func SUM(X) >= [X,X] >> X 156 | ---- 157 | 158 | Here we had a funcion `SUM` which takes two parameters of the same 159 | type and returns a value of the same type. It makes sense right ? 160 | 161 | In the following example we're using this feature just to show how 162 | parameters can be parametrized (use generics) as well. 163 | 164 | [source,groovy] 165 | ---- 166 | include::{testDir}/fnz/ast/type/FuncSpec.groovy[tags=multipleParameters,indent=0] 167 | ---- 168 | 169 | -------------------------------------------------------------------------------- /fnz-core/build.gradle: -------------------------------------------------------------------------------- 1 | // _____ _ _ 2 | // | __ \| | (_) 3 | // | |__) | |_ _ __ _ _ _ __ ___ 4 | // | ___/| | | | |/ _` | | '_ \/ __| 5 | // | | | | |_| | (_| | | | | \__ \ 6 | // |_| |_|\__,_|\__, |_|_| |_|___/ 7 | // __/ | 8 | // |___/ 9 | 10 | 11 | buildscript { 12 | repositories { 13 | jcenter() 14 | } 15 | dependencies { 16 | classpath 'com.jfrog.bintray.gradle:gradle-bintray-plugin:1.0' 17 | classpath 'net.saliman:gradle-cobertura-plugin:2.2.5' 18 | classpath 'com.tkruse.gradle:gradle-groovysh-plugin:1.0.4' 19 | } 20 | } 21 | 22 | apply plugin: 'java' 23 | apply plugin: 'groovy' 24 | apply plugin: 'codenarc' 25 | apply plugin: 'cobertura' 26 | apply plugin: 'maven' 27 | apply plugin: 'maven-publish' 28 | apply plugin: 'com.jfrog.bintray' 29 | apply plugin: 'com.github.tkruse.groovysh' 30 | 31 | // _____ _ _ 32 | // | __ \ | | (_) 33 | // | | | | ___ _ __ ___ _ __ __| | ___ _ __ ___ _ ___ ___ 34 | // | | | |/ _ \ '_ \ / _ \ '_ \ / _` |/ _ \ '_ \ / __| |/ _ \/ __| 35 | // | |__| | __/ |_) | __/ | | | (_| | __/ | | | (__| | __/\__ \ 36 | // |_____/ \___| .__/ \___|_| |_|\__,_|\___|_| |_|\___|_|\___||___/ 37 | // | | 38 | // |_| 39 | 40 | repositories { 41 | jcenter() 42 | } 43 | 44 | dependencies { 45 | compile 'org.codehaus.groovy:groovy-all:2.4.0' 46 | compile 'org.slf4j:slf4j-api:1.7.5' 47 | testCompile 'junit:junit:4.11' 48 | testCompile 'org.spockframework:spock-core:1.0-groovy-2.4' 49 | } 50 | 51 | // _____ __ _ _ _ 52 | // / ____| / _(_) | | (_) 53 | // | | ___ _ __ | |_ _ __ _ _ _ _ __ __ _| |_ _ ___ _ __ 54 | // | | / _ \| '_ \| _| |/ _` | | | | '__/ _` | __| |/ _ \| '_ \ 55 | // | |___| (_) | | | | | | | (_| | |_| | | | (_| | |_| | (_) | | | | 56 | // \_____\___/|_| |_|_| |_|\__, |\__,_|_| \__,_|\__|_|\___/|_| |_| 57 | // __/ | 58 | // |___/ 59 | 60 | codenarc { 61 | configFile = new File(projectDir, 'src/codenarc/rules.groovy') 62 | } 63 | 64 | codenarcTest.ignoreFailures = true 65 | 66 | cobertura { 67 | coverageFormats = ['html', 'xml'] 68 | } 69 | 70 | configurations.all { 71 | resolutionStrategy { 72 | // Cobertura includes an ASM version that can't handle Java 8, ASM 5.0.3 handles Java8 73 | force 'org.ow2.asm:asm:5.0.3' 74 | forcedModules = [ 'org.ow2.asm:asm:5.0.3' ] 75 | } 76 | } 77 | 78 | groovysh { 79 | groovyVersion = '2.4.0' 80 | } 81 | 82 | // _____ _ _ _ _ _ _ 83 | // | __ \(_) | | (_) | | | (_) 84 | // | | | |_ ___| |_ _ __ _| |__ _ _| |_ _ ___ _ __ 85 | // | | | | / __| __| '__| | '_ \| | | | __| |/ _ \| '_ \ 86 | // | |__| | \__ \ |_| | | | |_) | |_| | |_| | (_) | | | | 87 | // |_____/|_|___/\__|_| |_|_.__/ \__,_|\__|_|\___/|_| |_| 88 | // 89 | 90 | group = 'fnz' 91 | version = '1.0.6' 92 | 93 | publishing { 94 | publications { 95 | mavenCustom(MavenPublication) { 96 | groupId 'fnz' 97 | artifactId 'fnz' 98 | version '1.0.6' 99 | 100 | from components.java 101 | } 102 | } 103 | } 104 | 105 | bintray { 106 | user = bintrayUser 107 | key = bintrayKey 108 | 109 | publish = true 110 | publications = ['mavenCustom'] 111 | pkg { 112 | name = 'fnz' 113 | repo = "maven" 114 | version { 115 | name = '1.0.6' 116 | } 117 | } 118 | 119 | } 120 | 121 | // _____ _ _ _ 122 | // / ____| | | | | | | 123 | // | | _ _ ___| |_ ___ _ __ ___ | |_ __ _ ___| | _____ 124 | // | | | | | / __| __/ _ \| '_ ` _ \ | __/ _` / __| |/ / __| 125 | // | |___| |_| \__ \ || (_) | | | | | | | || (_| \__ \ <\__ \ 126 | // \_____\__,_|___/\__\___/|_| |_| |_| \__\__,_|___/_|\_\___/ 127 | // 128 | 129 | 130 | task('console', dependsOn:'classes', type: JavaExec) { 131 | group = 'help' 132 | description 'starts a Groovy console with the classpath of the current project' 133 | main='groovy.ui.Console' 134 | project.dependencies.add("compile", 'org.codehaus.groovy:groovy-console:2.4.0') 135 | classpath = sourceSets.main.runtimeClasspath 136 | } 137 | -------------------------------------------------------------------------------- /fnz-core/src/test/groovy/fnz/FnzSpec.groovy: -------------------------------------------------------------------------------- 1 | package fnz 2 | 3 | import static Fnz.bind 4 | import static Fnz.fmap 5 | import static Fnz.Just 6 | import static Fnz.wrap 7 | import static Fnz.val 8 | import static Fnz.Maybe 9 | import static Fnz.Failure 10 | import static Fnz.Success 11 | 12 | import fnz.data.Try 13 | import fnz.data.Function 14 | import fnz.data.Maybe 15 | 16 | import spock.lang.Unroll 17 | import spock.lang.Specification 18 | 19 | class FnzSpec extends Specification { 20 | 21 | void 'Fmap'() { 22 | given: 'a function (a->b) using functional interface coertion' 23 | Function fn = 24 | { String word -> return word.length() } as Function 25 | when: 'applying fmap::(a->b) -> fa -> fb' 26 | Maybe.Just result = fmap(Just("hi"), fn) 27 | then: 'result should be the expected' 28 | result instanceof Maybe.Just 29 | result.isPresent() 30 | val(result) == 2 31 | } 32 | 33 | void 'Binding'() { 34 | when: 'Building a nested binding expression' 35 | Maybe.Just result = 36 | bind(Just(1)) { Integer x -> 37 | bind(Just(x + 1)) { Integer y -> 38 | Just(y + 1) 39 | } 40 | } 41 | then: 'Result should be 2 more' 42 | result instanceof Maybe.Just 43 | result.isPresent() 44 | val(result) == 3 45 | } 46 | 47 | void 'Using bind with a list: looks like comprehensions'() { 48 | when: 'using a list' 49 | def numbers = 50 | [1, 2, 3, 4] 51 | .fmap { x -> 52 | Just([x, x + 1]) 53 | } 54 | then: 'we should get the expected sequence' 55 | numbers*.get() == [[1, 2], [2, 3], [3, 4], [4, 5]] 56 | } 57 | 58 | void 'using maybe method: monadic value'() { 59 | given: 'a function to increment a given number' 60 | def inc = { x -> x + 1 } 61 | when: 'trying to apply the computation' 62 | def tryResult = 63 | val(fmap(Just(value), wrap(inc))) 64 | then: 'there could be a result or not' 65 | Maybe(tryResult).isPresent() == isPresent 66 | and: 'possible final value should be' 67 | val(Maybe(tryResult)) == finalValue 68 | where: 'possible values are' 69 | value | isPresent | finalValue 70 | null | false | null 71 | 2 | true | 3 72 | } 73 | 74 | void 'using maybe method: simple value'() { 75 | given: 'a function to increment a given number' 76 | def inc = { x -> x + 1 } 77 | when: 78 | Maybe result = fmap(Maybe(value), inc) 79 | then: 'possible final value should be' 80 | result.isPresent() == isPresent 81 | val(result) == finalValue 82 | where: 'possible values are' 83 | value | isPresent | finalValue 84 | null | false | null 85 | 2 | true | 3 86 | } 87 | 88 | void 'using Failure()'() { 89 | when: 'trying to use a failure to compute anything' 90 | Try result = fmap(Failure(), { x -> x }) 91 | then: 'we should not be able' 92 | result instanceof Try.Failure 93 | val(result) == null 94 | result.exception instanceof NullPointerException 95 | } 96 | 97 | void 'using Success()'() { 98 | when: 'trying to use a success value to compute anything' 99 | Try result = fmap(Success(1)) { x -> x + 1 } 100 | then: 101 | result instanceof Try.Success 102 | val(result) == 2 103 | } 104 | 105 | void 'using val with null'() { 106 | expect: 'a call with null returns null' 107 | !val(null) 108 | } 109 | 110 | void 'stupid coverage check about creating an instance of final class'() { 111 | expect: 112 | new Fnz() 113 | } 114 | 115 | @Unroll 116 | void 'alternative way of getting wrapped value'() { 117 | expect: 'the expected output' 118 | Fnz.get(input) == output 119 | where: 'possible inputs are' 120 | input | output 121 | null | null 122 | Maybe(null) | null 123 | 1 | 1 124 | Maybe(1) | 1 125 | Maybe(Maybe(null)) | null 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /fnz-site/src/site/README.md: -------------------------------------------------------------------------------- 1 | ![Banner](https://raw.githubusercontent.com/sysgears/grain-theme-portfolio/master/banner.jpg) 2 | 3 | #Grain Bootstrap Stylish Portfolio Theme 4 | 5 | This [Grain] theme is based on Start Bootstrap [Stylish Portfolio Template]. 6 | 7 | Please, refer to the [documentation][documentation] for further details. 8 | 9 | Contributing 10 | ============ 11 | 12 | Any person or company wanting to contribute to this project should follow 13 | the following rules in order to their contribution being accepted. 14 | 15 | Sign your Work 16 | -------------- 17 | 18 | We require that all contributors "sign-off" on their commits. This 19 | certifies that the contribution is your original work, or you have rights to 20 | submit it under the same license, or a compatible license. 21 | 22 | Any contribution which contains commits that are not Signed-Off will not be 23 | accepted. 24 | 25 | To sign off on a commit you simply use the `--signoff` (or `-s`) option when 26 | committing your changes: 27 | 28 | $ git commit -s -m "Adding a new widget driver for cogs." 29 | 30 | This will append the following to your commit message: 31 | 32 | Signed-off-by: Your Name 33 | 34 | By doing this you certify the below: 35 | 36 | Developer's Certificate of Origin 1.1 37 | 38 | If you wish to add the signoff to the commit message on your every commit 39 | without the need to specify -s or --signoff, rename 40 | .git/hooks/commit-msg.sample to .git/hooks/commit-msg and uncomment the lines: 41 | 42 | ``` sh 43 | SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p') 44 | grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1" 45 | ``` 46 | 47 | Developer's Certificate of Origin 48 | --------------------------------- 49 | 50 | To help track the author of a patch as well as the submission chain, 51 | and be clear that the developer has authority to submit a patch for 52 | inclusion into this project please sign off your work. The sign off 53 | certifies the following: 54 | 55 | Developer's Certificate of Origin 1.1 56 | 57 | By making a contribution to this project, I certify that: 58 | 59 | (a) The contribution was created in whole or in part by me and I 60 | have the right to submit it under the open source license 61 | indicated in the file; or 62 | 63 | (b) The contribution is based upon previous work that, to the best 64 | of my knowledge, is covered under an appropriate open source 65 | license and I have the right under that license to submit that 66 | work with modifications, whether created in whole or in part 67 | by me, under the same open source license (unless I am 68 | permitted to submit under a different license), as indicated 69 | in the file; or 70 | 71 | (c) The contribution was provided directly to me by some other 72 | person who certified (a), (b) or (c) and I have not modified 73 | it. 74 | 75 | (d) I understand and agree that this project and the contribution 76 | are public and that a record of the contribution (including all 77 | personal information I submit with it, including my sign-off) is 78 | maintained indefinitely and may be redistributed consistent with 79 | this project or the open source license(s) involved. 80 | 81 | (e) I hereby grant to the project, SysGears, LLC and its successors; 82 | and recipients of software distributed by the Project a perpetual, 83 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 84 | copyright license to reproduce, modify, prepare derivative works of, 85 | publicly display, publicly perform, sublicense, and distribute this 86 | contribution and such modifications and derivative works consistent 87 | with this Project, the open source license indicated in the previous 88 | work or other appropriate open source license specified by the Project 89 | and approved by the Open Source Initiative(OSI) 90 | at http://www.opensource.org. 91 | 92 | License 93 | ======= 94 | 95 | Grain Bootstrap Theme Stylish Portfolio is licensed under the terms of the 96 | [Apache License, Version 2.0][Apache License, Version 2.0]. 97 | 98 | [Grain]: http://sysgears.com/grain/ 99 | 100 | [Apache License, Version 2.0]: http://www.apache.org/licenses/LICENSE-2.0.html 101 | [Developer Certificate of Origin]: https://raw.github.com/sysgears/grain/master/DCO 102 | [Stylish Portfolio Template]: http://startbootstrap.com/template-overviews/stylish-portfolio/ 103 | [documentation]: http://sysgears.com/grain/docs/latest/ -------------------------------------------------------------------------------- /fnz-core/src/main/groovy/fnz/ast/flow/LetAstTransformer.groovy: -------------------------------------------------------------------------------- 1 | package fnz.ast.flow 2 | 3 | import static org.codehaus.groovy.ast.ClassHelper.make 4 | import static org.codehaus.groovy.ast.tools.GeneralUtils.args 5 | import static org.codehaus.groovy.ast.tools.GeneralUtils.params 6 | import static org.codehaus.groovy.ast.tools.GeneralUtils.param 7 | import static org.codehaus.groovy.ast.tools.GeneralUtils.closureX 8 | import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt 9 | 10 | import static fnz.ast.AstUtils.applyScopeVisitor 11 | import static fnz.ast.AstUtils.callClosureX 12 | import static fnz.ast.AstUtils.getArgs 13 | import static fnz.ast.AstUtils.getFirstArgumentAs 14 | import static fnz.ast.AstUtils.getLastArgumentAs 15 | 16 | import fnz.ast.MethodCallExpressionTransformer 17 | import groovy.transform.CompileStatic 18 | import groovy.transform.CompileDynamic 19 | import org.codehaus.groovy.ast.VariableScope 20 | 21 | import org.codehaus.groovy.ast.expr.Expression 22 | import org.codehaus.groovy.ast.expr.ArgumentListExpression 23 | import org.codehaus.groovy.ast.expr.MapEntryExpression 24 | import org.codehaus.groovy.ast.expr.MapExpression 25 | import org.codehaus.groovy.ast.expr.ConstantExpression 26 | import org.codehaus.groovy.ast.expr.ClosureExpression 27 | import org.codehaus.groovy.ast.expr.MethodCallExpression 28 | 29 | import org.codehaus.groovy.ast.stmt.Statement 30 | import org.codehaus.groovy.control.SourceUnit 31 | 32 | /** 33 | * 34 | * This transformer will transform expressions like this one: 35 | *
 36 |  * let(x:1, y: { x + 1}) {
 37 |  *     y + 1
 38 |  * }
 39 |  * 
40 | * into this one: 41 | *
 42 |  * { x ->
 43 |  *   { y ->
 44 |  *     { y + 1}()
 45 |  *   }({x + 1}())
 46 |  * }(1)
 47 |  * 
48 | * 49 | * @author Mario Garcia 50 | * 51 | */ 52 | @CompileStatic 53 | @SuppressWarnings('FactoryMethodName') 54 | class LetAstTransformer extends MethodCallExpressionTransformer { 55 | 56 | static final String LET_METHOD_NAME = 'let' 57 | 58 | LetAstTransformer(SourceUnit sourceUnit) { 59 | super(sourceUnit, LET_METHOD_NAME) 60 | } 61 | 62 | Expression transformMethodCall(final MethodCallExpression methodCallExpression) { 63 | ArgumentListExpression args = getArgs(methodCallExpression) 64 | MapExpression mapExpression = getFirstArgumentAs(args, MapExpression) 65 | List mapEntryExpressions = mapExpression.mapEntryExpressions.reverse() 66 | ClosureExpression fn = getLastArgumentAs(args, ClosureExpression) 67 | 68 | // checking if there is another nested let expression 69 | this.visitClosureExpression(fn) 70 | // processing this let expression 71 | MethodCallExpression result = 72 | (MethodCallExpression) mapEntryExpressions.inject(fn, this.&evaluateMapEntryExpression) 73 | // fixing variable scope 74 | applyScopeVisitor(result, sourceUnit) 75 | 76 | return result 77 | } 78 | 79 | @CompileDynamic 80 | private Expression evaluateMapEntryExpression(final Expression previous, final MapEntryExpression next) { 81 | ConstantExpression nextKey = (ConstantExpression) next.keyExpression 82 | String closureVarName = nextKey.value.toString() 83 | Expression nextValue = next.valueExpression 84 | Statement previousStatement = createStatementFrom(previous) 85 | Expression processedValue = processValueExpression(nextValue) 86 | ClosureExpression closureExpression = createClosure(closureVarName, previousStatement) 87 | 88 | return callClosureX(closureExpression, args(processedValue)) 89 | } 90 | 91 | ClosureExpression createClosure(final String singleParamName, final Statement block) { 92 | ClosureExpression closureExpression = closureX(params(param(make(Object), singleParamName)), block) 93 | closureExpression.variableScope = new VariableScope() 94 | 95 | return closureExpression 96 | } 97 | 98 | Statement createStatementFrom(final Expression expression) { 99 | return stmt(expression) 100 | } 101 | 102 | Statement createStatementFrom(final ClosureExpression expression) { 103 | return stmt(callClosureX(expression)) 104 | } 105 | 106 | Expression processValueExpression(final Expression expression) { 107 | return expression 108 | } 109 | 110 | Expression processValueExpression(final ClosureExpression expression) { 111 | return callClosureX(expression) 112 | } 113 | 114 | } 115 | -------------------------------------------------------------------------------- /fnz-core/src/main/groovy/fnz/ast/flow/LetmAstTransformer.groovy: -------------------------------------------------------------------------------- 1 | package fnz.ast.flow 2 | 3 | import static org.codehaus.groovy.ast.ClassHelper.make 4 | import static org.codehaus.groovy.ast.tools.GeneralUtils.callX 5 | import static org.codehaus.groovy.ast.tools.GeneralUtils.block 6 | import static org.codehaus.groovy.ast.tools.GeneralUtils.stmt 7 | import static org.codehaus.groovy.ast.tools.GeneralUtils.args 8 | import static org.codehaus.groovy.ast.tools.GeneralUtils.param 9 | import static org.codehaus.groovy.ast.tools.GeneralUtils.params 10 | import static org.codehaus.groovy.ast.tools.GeneralUtils.closureX 11 | 12 | import static fnz.ast.AstUtils.applyScopeVisitor 13 | import static fnz.ast.AstUtils.getArgs 14 | import static fnz.ast.AstUtils.getFirstArgumentAs 15 | import static fnz.ast.AstUtils.getLastArgumentAs 16 | 17 | import fnz.ast.MethodCallExpressionTransformer 18 | import groovy.transform.CompileStatic 19 | import groovy.transform.CompileDynamic 20 | import org.codehaus.groovy.ast.VariableScope 21 | 22 | import org.codehaus.groovy.ast.expr.Expression 23 | import org.codehaus.groovy.ast.expr.ConstantExpression 24 | import org.codehaus.groovy.ast.expr.MethodCallExpression 25 | import org.codehaus.groovy.ast.expr.ArgumentListExpression 26 | import org.codehaus.groovy.ast.expr.MapExpression 27 | import org.codehaus.groovy.ast.expr.ClosureExpression 28 | import org.codehaus.groovy.ast.expr.MapEntryExpression 29 | 30 | import org.codehaus.groovy.ast.stmt.Statement 31 | import org.codehaus.groovy.control.SourceUnit 32 | 33 | /** 34 | * 35 | * This transformer will transform expressions like this one: 36 | *
 37 |  * letm(x:Just(1), y: { Just(x + 1) } ) {
 38 |  *     Just(y + 1)
 39 |  * }
 40 |  * 
41 | * into this one: 42 | *
 43 |  * bind(Just(1)) { x ->
 44 |  *    bind({ Just(x + 1) }()) { y ->
 45 |  *        Just(y + 1)
 46 |  *    }
 47 |  * }
 48 |  *
49 | * 50 | * The deepest expression should always return a fnz.data.Monad instance 51 | * (of course or any of its children). 52 | * 53 | * @author Mario Garcia 54 | * 55 | */ 56 | @CompileStatic 57 | @SuppressWarnings('FactoryMethodName') 58 | class LetmAstTransformer extends MethodCallExpressionTransformer { 59 | 60 | static final String LET_METHOD_NAME = 'letm' 61 | static final String BIND_METHOD_NAME = 'bind' 62 | static final String DO_CALL_METHOD_NAME = 'doCall' 63 | 64 | LetmAstTransformer(SourceUnit sourceUnit) { 65 | super(sourceUnit, LET_METHOD_NAME) 66 | } 67 | 68 | Expression transformMethodCall(final MethodCallExpression methodCallExpression) { 69 | ArgumentListExpression args = getArgs(methodCallExpression) 70 | MapExpression mapExpression = getFirstArgumentAs(args, MapExpression) 71 | List mapEntryExpressions = mapExpression.mapEntryExpressions.reverse() 72 | ClosureExpression fn = getLastArgumentAs(args, ClosureExpression) 73 | 74 | MethodCallExpression finalExpression = loopThroughEntryExpressions(mapEntryExpressions, fn) 75 | 76 | this.visitClosureExpression(fn) 77 | applyScopeVisitor(finalExpression, sourceUnit) 78 | 79 | return finalExpression 80 | } 81 | 82 | private MethodCallExpression loopThroughEntryExpressions( 83 | final List expressions, 84 | final ClosureExpression fn) { 85 | return (MethodCallExpression) expressions.inject(fn, this.&evaluateMapEntryExpression) 86 | } 87 | 88 | @CompileDynamic 89 | private Expression evaluateMapEntryExpression(final Expression previous, final MapEntryExpression next) { 90 | ConstantExpression nextKey = (ConstantExpression) next.keyExpression 91 | String closureVarName = nextKey.value.toString() 92 | Expression nextValue = next.valueExpression 93 | Statement stmt = createStatementFrom(previous) 94 | ClosureExpression closureExpression = createClosure(closureVarName, stmt) 95 | 96 | return getBindExpression(nextValue, closureExpression) 97 | } 98 | 99 | ClosureExpression createClosure(final String singleParamName, final Statement block) { 100 | ClosureExpression closureExpression = closureX(params(param(make(Object), singleParamName)), block) 101 | closureExpression.variableScope = new VariableScope() 102 | 103 | return closureExpression 104 | } 105 | 106 | Statement createStatementFrom(final Expression expression) { 107 | return block(stmt(expression)) 108 | } 109 | 110 | Statement createStatementFrom(final ClosureExpression expression) { 111 | return expression.code 112 | } 113 | 114 | private MethodCallExpression getBindExpression( 115 | final Expression value, final ClosureExpression closureWithKey) { 116 | return value instanceof ClosureExpression ? 117 | callX(callX(value, DO_CALL_METHOD_NAME), BIND_METHOD_NAME, args(closureWithKey)) : 118 | callX(value, BIND_METHOD_NAME, args(closureWithKey)) 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/src/com/sysgears/theme/deploy/GHPagesDeployer.groovy: -------------------------------------------------------------------------------- 1 | package com.sysgears.theme.deploy 2 | 3 | import com.sysgears.grain.taglib.Site 4 | 5 | /** 6 | * Provides deploying of the generated site to GitHub Pages service. 7 | */ 8 | class GHPagesDeployer { 9 | 10 | /** 11 | * Site reference, provides access to site configuration. 12 | */ 13 | private final Site site 14 | 15 | public GHPagesDeployer(Site site) { 16 | this.site = site 17 | } 18 | 19 | /** 20 | * Deploys generated site. 21 | */ 22 | def deploy = { 23 | def cacheDir = site.cache_dir 24 | def destinationDir = site.destination_dir 25 | def ghPagesUrl = site.gh_pages_url 26 | 27 | def ant = new AntBuilder() 28 | 29 | if (!ghPagesUrl) { 30 | ant.echo('Couldn\'t upload to GitHub Pages, please specify your GitHub repo url first') 31 | return 32 | } 33 | 34 | def isUserPage = ghPagesUrl.toString().endsWith('.github.io.git') 35 | def workingBranch = isUserPage ? 'master' : 'gh-pages' 36 | def cacheDeployDir = "$cacheDir/gh-deploy" 37 | 38 | def ghPagesExists = !isUserPage ? ghPagesBranchExists(ant, ghPagesUrl) : null 39 | 40 | def git = { List args -> 41 | ant.exec(executable: 'git', dir: cacheDeployDir) { 42 | args.collect { arg(value: it) } 43 | } 44 | } 45 | 46 | ant.sequential { 47 | ant.delete(dir: cacheDeployDir, failonerror: false) 48 | ant.mkdir(dir: cacheDeployDir) 49 | git(['init']) 50 | if (!isUserPage && !ghPagesExists) { 51 | git(['remote', 'add', '-f', 'origin', ghPagesUrl]) 52 | git(['checkout', '-b', workingBranch]) 53 | } else { 54 | git(['remote', 'add', '-t', workingBranch, '-f', 'origin', ghPagesUrl]) 55 | git(['checkout', workingBranch]) 56 | ant.delete(includeEmptyDirs: true) { 57 | fileset(dir: cacheDeployDir) { 58 | exclude(name: '.git') 59 | } 60 | } 61 | } 62 | ant.copy(todir: cacheDeployDir) { 63 | fileset(dir: destinationDir) 64 | } 65 | git(['add', '.']) 66 | } 67 | 68 | def filesToBeDeleted = getListOfDeletedFiles(ant, cacheDeployDir) 69 | 70 | def continueDeploy = filesToBeDeleted.isEmpty() ?: askContinueDeploy(ant, filesToBeDeleted) 71 | 72 | ant.sequential { 73 | if (continueDeploy) { 74 | if (!filesToBeDeleted.isEmpty()) { 75 | git(['add', '-u']) 76 | } 77 | git(['commit', '-m', 'Updated site']) 78 | git(['push', 'origin', "$workingBranch:$workingBranch"]) 79 | } 80 | ant.delete(dir: cacheDeployDir) 81 | } 82 | } 83 | 84 | /** 85 | * Determines whether gh-pages branch exists for given repo. 86 | * 87 | * @param ant AntBuilder instance 88 | * @param ghPagesUrl GitHub repo url 89 | * @return true if branch exists, false otherwise 90 | */ 91 | private def ghPagesBranchExists(AntBuilder ant, String ghPagesUrl) { 92 | ant.exec(executable: 'git', outputproperty: 'gitLsOutput') { 93 | ['ls-remote', '--heads', ghPagesUrl].collect { arg(value: it) } 94 | } 95 | ant.project.properties.gitLsOutput.contains('refs/heads/gh-pages') 96 | } 97 | 98 | /** 99 | * Returns the list of files to be deleted after deploy. 100 | * 101 | * @param ant AntBuilder instance 102 | * @param cacheDeployDir cache deploy dir 103 | * @return list of files 104 | */ 105 | private def getListOfDeletedFiles(AntBuilder ant, String cacheDeployDir) { 106 | ant.exec(executable: 'git', outputproperty: 'gitStatusOutput', dir: cacheDeployDir) { 107 | arg(value: 'status') 108 | } 109 | def gStatus = ant.project.properties.gitStatusOutput 110 | def filesToBeDeleted = [] 111 | gStatus.eachLine { 112 | def matcher = it =~ /#\s+deleted:\s+(.+)/ 113 | if (matcher.matches()) { 114 | filesToBeDeleted << matcher[0][1] 115 | } 116 | } 117 | 118 | filesToBeDeleted 119 | } 120 | 121 | /** 122 | * Asks whether user wants to continue deploy. 123 | * 124 | * @param ant AntBuilder instance 125 | * @param filesToBeDeleted list of the files to be removed 126 | * @return true, if user confirms he wants to continue deploy, false otherwise 127 | */ 128 | private def askContinueDeploy(AntBuilder ant, List filesToBeDeleted) { 129 | def fileList = new StringBuilder() 130 | filesToBeDeleted.each { fileList << "$it\n" } 131 | def message = "Files to be deleted from the repo:\n${fileList}Сontinue deploy?" 132 | ant.input(message: message, validargs: 'y,n', addProperty: 'answer') 133 | def answer = ant.project.properties.answer 134 | answer.equals('y') 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /fnz-site/src/site/.cache/markup/asciidoctor.0_1_4/6031286f4f22df21a61df018a5312a7f.html: -------------------------------------------------------------------------------- 1 |
2 |
    3 |
  • 4 |

    secure

    5 |
  • 6 |
  • 7 |

    one

    8 |
  • 9 |
  • 10 |

    two

    11 |
  • 12 |
  • 13 |

    three

    14 |
  • 15 |
16 |
17 |
18 |

la

19 |
20 |
21 |

package fnz

22 |
23 |
24 |

import static Fnz.bind 25 | import static Fnz.fmap 26 | import static Fnz.Just 27 | import static Fnz.List 28 | import static Fnz.wrap 29 | import static Fnz.val 30 | import static Fnz.Maybe 31 | import static Fnz.Failure 32 | import static Fnz.Success

33 |
34 |
35 |

import fnz.data.Try 36 | import fnz.data.Function 37 | import fnz.data.Maybe 38 | import fnz.data.ListMonad

39 |
40 |
41 |

import spock.lang.Specification

42 |
43 |
44 |

class FnzSpec extends Specification {

45 |
46 |
47 |
48 |
void 'Fmap'() {
 49 |     given: 'a function (a->b) using functional interface coertion'
 50 |         Function<String,Integer> fn =
 51 |             { String word -> return word.length() } as Function<String,Integer>
 52 |     when: 'applying fmap::(a->b) -> fa -> fb'
 53 |         Maybe.Just<Integer> result = fmap(Just("hi"), fn)
 54 |     then: 'result should be the expected'
 55 |         result instanceof Maybe.Just
 56 |         result.isPresent()
 57 |         val(result) == 2
 58 | }
59 |
60 |
61 |
62 |
63 |
void 'Binding'() {
 64 |     when: 'Building a nested binding expression'
 65 |         Maybe.Just<Integer> result =
 66 |             bind(Just(1)) { Integer x ->
 67 |                 bind(Just(x + 1)) { Integer y ->
 68 |                     Just(y + 1)
 69 |                 }
 70 |             }
 71 |     then: 'Result should be 2 more'
 72 |         result instanceof Maybe.Just
 73 |         result.isPresent()
 74 |         val(result) == 3
 75 | }
76 |
77 |
78 |
79 |
80 |
void 'Using bind with a list monad: looks like comprehensions'() {
 81 |     given: 'a list monad'
 82 |         ListMonad<Integer> numbers = List(1, 2, 3, 4)
 83 |     when: 'applying a function to bind'
 84 |         ListMonad<Integer> result =
 85 |             bind(numbers){ x -> [x, x + 1] as ListMonad }
 86 |     then: 'we should get the expected sequence'
 87 |         result.typedRef.value == [1, 2, 2, 3, 3, 4, 4, 5]
 88 | }
89 |
90 |
91 |
92 |
93 |
void 'using maybe method: monadic value'() {
 94 |     given: 'a function to increment a given number'
 95 |         def inc = { x ->  x + 1 }
 96 |     when: 'trying to apply the computation'
 97 |         def tryResult =
 98 |             val(fmap(Just(value), wrap(inc)))
 99 |     then: 'there could be a result or not'
100 |         Maybe(tryResult).isPresent() == isPresent
101 |     and: 'possible final value should be'
102 |         val(Maybe(tryResult)) == finalValue
103 |     where: 'possible values are'
104 |         value | isPresent | finalValue
105 |         null  | false     | null
106 |         2     | true      | 3
107 | }
108 |
109 |
110 |
111 |
112 |
void 'using maybe method: simple value'() {
113 |     given: 'a function to increment a given number'
114 |         def inc = { x ->  x + 1 }
115 |     when:
116 |         Maybe result = fmap(Maybe(value), inc)
117 |     then: 'possible final value should be'
118 |         result.isPresent() == isPresent
119 |         val(result) == finalValue
120 |     where: 'possible values are'
121 |         value | isPresent | finalValue
122 |         null  | false     | null
123 |         2     | true      | 3
124 | }
125 |
126 |
127 |
128 |
129 |
void 'using Failure()'() {
130 |     when: 'trying to use a failure to compute anything'
131 |         Try result = fmap(Failure(), { x -> x })
132 |     then: 'we should not be able'
133 |         result instanceof Try.Failure
134 |         val(result) == null
135 |         result.exception instanceof NullPointerException
136 | }
137 |
138 |
139 |
140 |
141 |
void 'using Success()'() {
142 |     when: 'trying to use a success value to compute anything'
143 |         Try result = fmap(Success(1)) { x -> x + 1 }
144 |     then:
145 |         result instanceof Try.Success
146 |         val(result) == 2
147 | }
148 |
149 |
150 |
151 |
152 |
void 'using val with null'() {
153 |     expect: 'a call with null returns null'
154 |     !val(null)
155 | }
156 |
157 |
158 |
159 |
160 |
void 'stupid coverage check about creating an instance of final class'() {
161 |     expect:
162 |     new Fnz()
163 | }
164 |
165 |
166 |
167 |

}

168 |
169 |
170 |
-------------------------------------------------------------------------------- /fnz-site/src/site/content/index.html: -------------------------------------------------------------------------------- 1 | --- 2 | layout: default 3 | title: Stylish Portfolio Theme 4 | navigation: 5 | title: FNZ 6 | sections: 7 | - id: top 8 | name: Home 9 | - id: about 10 | name: About 11 | - id: examples 12 | name: Getting Started 13 | - id: checkdocs 14 | name: Show me the code! 15 | header: 16 | title: 'FλZ' 17 | subtitle: Groovy functional library 18 | button: Find Out More 19 | link: '#about' 20 | about: 21 | title: FNZ adds functional abstractions to Groovy language! 22 | text: Use functional abstractions such as Maybe, Either, handling exception cases with Try...
Keep reading or go to
docs. 23 | services: 24 | title: Getting Started 25 | types: 26 | # The 'image' fields below specify font-awesome class name for icons. 27 | # For available font awesome icons please check documentation at: http://fortawesome.github.io/Font-Awesome/icons/ 28 | - name: Tutorial 29 | image: fa-gear 30 | text: Lorem ipsum dolor sit amet, consectetur adipisicing elit. 31 | modal: serviceModal1 32 | - name: Documentation 33 | image: fa-book 34 | text: Lorem ipsum dolor sit amet, consectetur adipisicing elit. 35 | modal: serviceModal2 36 | - name: Source code 37 | image: fa-github 38 | text: Lorem ipsum dolor sit amet, consectetur adipisicing elit. 39 | modal: serviceModal3 40 | - name: Service Name 41 | image: fa-shield 42 | text: Lorem ipsum dolor sit amet, consectetur adipisicing elit. 43 | modal: serviceModal4 44 | callout: 45 | text: Vertically Centered Text 46 | portfolio: 47 | title: Our Work 48 | button: View More Items 49 | buttonlink: '#' 50 | 51 | projects: 52 | - image: images/portfolio-1.jpg 53 | modal: portfolioModal1 54 | - image: images/portfolio-2.jpg 55 | modal: portfolioModal2 56 | - image: images/portfolio-3.jpg 57 | modal: portfolioModal3 58 | - image: images/portfolio-4.jpg 59 | modal: portfolioModal4 60 | modals: 61 | - id: portfolioModal1 62 | title: Project Title 63 | text: Use this area of the page to describe your project. The icon above is part of a free icon set by Flat Icons. On their website, you can download their free set with 16 icons, or you can purchase the entire set with 146 icons for only $12! 64 | image: images/portfolio-1.jpg 65 | client: Start Bootstrap 66 | date: April 2014 67 | link: http://startbootstrap.com 68 | service: Web Development 69 | 70 | - id: portfolioModal2 71 | title: Project Title 72 | text: Use this area of the page to describe your project. The icon above is part of a free icon set by Flat Icons. On their website, you can download their free set with 16 icons, or you can purchase the entire set with 146 icons for only $12! 73 | image: images/portfolio-2.jpg 74 | client: Start Bootstra 75 | date: April 2014 76 | link: http://startbootstrap.com 77 | service: Web Development 78 | 79 | - id: portfolioModal3 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | title: Project Title 96 | text: Use this area of the page to describe your project. The icon above is part of a free icon set by Flat Icons. On their website, you can download their free set with 16 icons, or you can purchase the entire set with 146 icons for only $12! 97 | image: images/portfolio-3.jpg 98 | client: Start Bootstrap 99 | date: April 2014 100 | link: http://startbootstrap.com 101 | service: Web Development 102 | 103 | - id: portfolioModal4 104 | title: Project Title 105 | text: Use this area of the page to describe your project. The icon above is part of a free icon set by Flat Icons. On their website, you can download their free set with 16 icons, or you can purchase the entire set with 146 icons for only $12! 106 | image: images/portfolio-4.jpg 107 | client: Start Bootstrap 108 | date: April 2014 109 | link: http://startbootstrap.com 110 | service: Web Development 111 | calltoaction: 112 | text: The buttons below are impossible to resist. 113 | button1: Click Me! 114 | link1: '#' 115 | button2: Look at Me! 116 | link2: '#' 117 | map: 118 | link: https://maps.google.com/maps?f=q&source=s_q& hl=en&geocode=&q=Twitter,+Inc.,+Market+Street,+San+Francisco,+CA&aq=0&oq=twitter&sll=28.659344,-81.187888&sspn=0.128789,0.264187&ie=UTF8&hq=Twitter,+Inc.,+Market+Street,+San+Francisco,+CA&t=m&z=15&iwloc=A&output=embed 119 | footer: 120 | title: Start Bootstrap 121 | adress: 3481 Melrose Place
Beverly Hills, CA 90210 122 | phone: (123) 456-7890 123 | mail: name@example.com 124 | link_facebook: '#' 125 | link_twitter: '#' 126 | link_dribbble: '#' 127 | copyright: FNZ 2015 128 | --- 129 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /fnz-core/src/main/java/fnz/data/Try.java: -------------------------------------------------------------------------------- 1 | package fnz.data; 2 | 3 | /** 4 | * 5 | * @param The contained type 6 | */ 7 | public abstract class Try extends MonadType implements Or> { 8 | 9 | protected Try(Type valueRef) { 10 | super(valueRef); 11 | } 12 | 13 | public abstract Boolean isSuccess(); 14 | public abstract Boolean isFailure() ; 15 | 16 | public static class Success extends Try { 17 | 18 | private Success(Type value) { 19 | super(value); 20 | } 21 | 22 | @Override 23 | public Boolean isSuccess() { 24 | return true; 25 | } 26 | 27 | @Override 28 | public Boolean isFailure() { 29 | return false; 30 | } 31 | 32 | @Override 33 | public > M bind(Function fn) { 34 | return fn.apply(getTypedRef().getValue()); 35 | } 36 | 37 | @Override 38 | public > M bind2(TypeAwareFunction fn) { 39 | return fn.apply((Class) this.getClass(), getTypedRef().getValue()); 40 | } 41 | 42 | @Override 43 | public Applicative fapply(Applicative> afn) { 44 | return this.fmap(afn.getTypedRef().getValue()); 45 | } 46 | 47 | @Override 48 | public > F fmap(Function fn) { 49 | try { 50 | return (F) success(fn.apply(getTypedRef().getValue())); 51 | } catch(Throwable th) { 52 | return (F) failure(getTypedRef(), th); 53 | } 54 | } 55 | 56 | @Override 57 | public Try or(Try alternative) { 58 | return this; 59 | } 60 | 61 | @Override 62 | public Try or(Function> alternative) { 63 | return this; 64 | } 65 | 66 | public static Success unit(A value) { 67 | return Try.success(value); 68 | } 69 | 70 | @Override 71 | public Boolean asBoolean() { 72 | return Boolean.TRUE; 73 | } 74 | 75 | } 76 | 77 | public static class Failure extends Try { 78 | 79 | private final Throwable throwable; 80 | 81 | private Failure(Type value, Throwable th) { 82 | super(value); 83 | this.throwable = th; 84 | } 85 | 86 | private Failure(Throwable throwable) { 87 | super(new Type(null)); 88 | this.throwable = throwable; 89 | } 90 | 91 | @Override 92 | public > M bind(Function fn) { 93 | return (M) new Try.Failure(getTypedRef(), throwable); 94 | } 95 | 96 | @Override 97 | public > M bind2(TypeAwareFunction fn) { 98 | return (M) new Try.Failure(getTypedRef(), throwable); 99 | } 100 | 101 | @Override 102 | public Applicative fapply(Applicative> afn) { 103 | return (Applicative) new Try.Failure(getTypedRef(), throwable); 104 | } 105 | 106 | @Override 107 | public > F fmap(Function fn) { 108 | return (F) new Try.Failure(getTypedRef(), throwable); 109 | } 110 | 111 | @Override 112 | public Boolean isFailure() { 113 | return true; 114 | } 115 | 116 | @Override 117 | public Boolean isSuccess() { 118 | return false; 119 | } 120 | 121 | public void throwException() throws Throwable { 122 | throw this.throwable; 123 | } 124 | 125 | public Throwable getException() { 126 | return this.throwable; 127 | } 128 | 129 | @Override 130 | public Try or(Try alternative) { 131 | return alternative; 132 | } 133 | 134 | @Override 135 | public Try or(Function> alternative) { 136 | return alternative.apply(this.getTypedRef().getValue()); 137 | } 138 | 139 | @Override 140 | public Boolean asBoolean() { 141 | return Boolean.FALSE; 142 | } 143 | 144 | } 145 | 146 | public static Try.Success success(T value) { 147 | return new Try.Success(new Type(value)); 148 | } 149 | 150 | public static Try.Failure failure(T value) { 151 | return failure(value, new IllegalArgumentException("" + value)); 152 | } 153 | 154 | public static Try.Failure failure(T value, Throwable th) { 155 | return failure(new Type(value), th); 156 | } 157 | 158 | public static Try.Failure failure(Type value, Throwable th) { 159 | return new Try.Failure(value, th); 160 | } 161 | 162 | public static Try.Failure failure(Throwable th) { 163 | return new Try.Failure(th); 164 | } 165 | 166 | public static > Function> wrap(final F fn) { 167 | return new Function>() { 168 | public Try apply(A a) { 169 | return success(a).fmap(fn); 170 | } 171 | }; 172 | } 173 | 174 | public static Function> recover(final Function... alternatives) { 175 | return new Function>() { 176 | public Try apply(A a) { 177 | Try result = null; 178 | for (Function alternative : alternatives) { 179 | result = success(a).fmap(alternative); 180 | if (result.isSuccess()) { 181 | return result; 182 | } 183 | } 184 | return result; 185 | } 186 | }; 187 | } 188 | 189 | public static Try Try(Function fn) { 190 | return wrap(fn).apply(null); 191 | } 192 | 193 | public static Try Try(A a, Function fn) { 194 | return wrap(fn).apply(a); 195 | } 196 | 197 | } 198 | -------------------------------------------------------------------------------- /fnz-core/src/test/groovy/fnz/ast/flow/DoSpec.groovy: -------------------------------------------------------------------------------- 1 | package fnz.ast.flow 2 | 3 | import fnz.ast.FnzAst 4 | 5 | import fnz.test.AstBaseSpec 6 | import org.codehaus.groovy.control.CompilePhase 7 | 8 | class DoSpec extends AstBaseSpec { 9 | 10 | def exampleInstance 11 | 12 | void 'checking parameters'() { 13 | when: 'trying to use $do with wrong arguments' 14 | def helper = getScriptParser(FnzAst, CompilePhase.CONVERSION) 15 | helper.parse( 16 | ''' 17 | package fnz.ast.flow 18 | 19 | class A { 20 | void error() { 21 | $do(1) 22 | } 23 | } 24 | ''' 25 | ) 26 | then: 'it will throw an exception' 27 | thrown(Exception) 28 | } 29 | 30 | void 'normal expression with explicit return statement'() { 31 | when: 'trying to use $do with valid arguments' 32 | and: 'using an explicit monadic value return' 33 | def helper = getScriptParser(FnzAst, CompilePhase.CONVERSION) 34 | def exampleClass = 35 | helper.parse( 36 | ''' 37 | package fnz.ast.flow 38 | 39 | import fnz.Fnz 40 | import fnz.data.Maybe 41 | 42 | class A { 43 | Integer returningDoWithoutReturn() { 44 | Maybe result = 45 | $do { 46 | x = Maybe.just(1) 47 | y = Maybe.just(x + 1) 48 | z = { Maybe.just(y + 1) } 49 | 50 | return Maybe.just(z + 1) 51 | } 52 | 53 | return Fnz.val(result) 54 | } 55 | } 56 | ''' 57 | ) 58 | then: 'the result should be the expected' 59 | exampleClass.newInstance().returningDoWithoutReturn() == 4 60 | } 61 | 62 | void 'normal expression with $return'() { 63 | when: 'trying to use $do with valid arguments' 64 | and: 'using an implicit monadic value return' 65 | def helper = getScriptParser(FnzAst, CompilePhase.CONVERSION) 66 | def exampleClass = 67 | helper.parse( 68 | ''' 69 | package fnz.ast.flow 70 | 71 | // tag::simpleExample[] 72 | import fnz.Fnz 73 | import fnz.data.Maybe 74 | 75 | class A { 76 | Integer returningDoWithReturn() { 77 | Maybe result = 78 | $do { 79 | x = Maybe.just(1) 80 | y = Maybe.just(x + 1) 81 | z = Maybe.just(y) 82 | 83 | $return z + 1 84 | } 85 | 86 | return Fnz.val(result) 87 | } 88 | } 89 | // end::simpleExample[] 90 | ''' 91 | ) 92 | then: 'the result should be the expected' 93 | exampleClass.newInstance().returningDoWithReturn() == 3 94 | } 95 | 96 | void 'ommiting not assignment expressions'() { 97 | when: 'trying to use $do with valid arguments' 98 | and: 'using an implicit monadic value return' 99 | def helper = getScriptParser(FnzAst, CompilePhase.CONVERSION) 100 | def exampleClass = 101 | helper.parse( 102 | ''' 103 | package fnz.ast.flow 104 | 105 | import fnz.Fnz 106 | import fnz.data.Maybe 107 | 108 | class A { 109 | Integer returningDoWithReturn() { 110 | Maybe result = 111 | $do { 112 | x = Maybe.just(1) 113 | y = Maybe.just(x + 1) 114 | z = { Maybe.just(y + 1) } 115 | 116 | // other binary expressions are ommited 117 | p << 1 118 | 119 | $return z + 1 120 | } 121 | 122 | return Fnz.val(result) 123 | } 124 | } 125 | ''' 126 | ) 127 | then: 'the result should be the expected' 128 | exampleClass.newInstance().returningDoWithReturn() == 4 129 | } 130 | 131 | void 'using underscore to use non returning values expressions'() { 132 | when: 'trying to use $do with valid arguments' 133 | and: 'using an implicit monadic value return' 134 | def helper = getScriptParser(FnzAst, CompilePhase.CONVERSION) 135 | def exampleClass = 136 | helper.parse( 137 | ''' 138 | package fnz.ast.flow 139 | 140 | // tag::wildcard1[] 141 | import fnz.Fnz 142 | import fnz.data.Maybe 143 | 144 | class A { 145 | 146 | Maybe.Just PrintStrLn(Object val) { 147 | println val 148 | return Fnz.Just(val) 149 | } 150 | 151 | Integer returningDoWithReturn() { 152 | Maybe result = 153 | $do { 154 | x = Maybe.just(1) 155 | _ = PrintStrLn("x: $x") 156 | y = Maybe.just(x + 1) 157 | _ = PrintStrLn("y: $y") 158 | z = { Maybe.just(y + 1) } 159 | 160 | $return z + 1 161 | } 162 | 163 | return Fnz.val(result) 164 | } 165 | } 166 | // end::wildcard1[] 167 | ''' 168 | ) 169 | then: 'the result should be the expected' 170 | exampleClass.newInstance().returningDoWithReturn() == 4 171 | } 172 | 173 | } 174 | -------------------------------------------------------------------------------- /fnz-core/src/test/groovy/fnz/flow/WhereSpec.groovy: -------------------------------------------------------------------------------- 1 | package fnz.flow 2 | 3 | import static fnz.flow.Where.check 4 | import static fnz.Fnz.val 5 | 6 | import fnz.data.Maybe 7 | 8 | import spock.lang.Unroll 9 | import spock.lang.Specification 10 | 11 | class WhereSpec extends Specification { 12 | 13 | @Unroll 14 | void 'Build a where expression'() { 15 | when: 'An expression' 16 | // tag::simpleWhere[] 17 | Maybe result = check(weight: value) { 18 | when { weight <= underweight } then { "You're underweight" } 19 | when { weight <= normal } then { throw new Exception("You're normal") } 20 | when { weight <= fat } then { "You're fat" } 21 | otherwise { "You have a strange composition" } 22 | where { 23 | underweight = 50 24 | normal = 70 25 | fat = 90 26 | } 27 | } 28 | // end::simpleWhere[] 29 | then: 'Not exception has been thrown' 30 | notThrown(Exception) 31 | and: "The underweight output" 32 | result.isPresent() == true 33 | val(result) == expected 34 | where: 35 | value | expected 36 | 50 | "You're underweight" 37 | 90 | "You're fat" 38 | } 39 | 40 | void 'Throwing an exception'() { 41 | when: 'An expression' 42 | Maybe result = check(weight: 70) { 43 | when { weight <= underweight } then { "You're underweight" } 44 | when { weight <= normal } then { throw new Exception("You're normal") } 45 | when { weight <= fat } then { "You're fat" } 46 | otherwise { "You have a strange composition" } 47 | where { 48 | underweight = 50 49 | normal = 70 50 | fat = 90 51 | } 52 | } 53 | then: 'An exception has been thrown' 54 | thrown(Exception) 55 | } 56 | 57 | @Unroll 58 | void 'Shortening expressions [Only when-then]'() { 59 | when: 'There are only when-then expressions' 60 | // tag::whereOnlyWithWhenThen[] 61 | Maybe result = check(weight: value) { 62 | when { weight <= 51 } then { "You're underweight" } 63 | when { weight <= 90 } then { "You're fat" } 64 | } 65 | // end::whereOnlyWithWhenThen[] 66 | then: 'I should be able to get the rigth result' 67 | val(result) == expected 68 | where: 'The test value is' 69 | value | expected 70 | 51 | "You're underweight" 71 | 90 | "You're fat" 72 | } 73 | 74 | @Unroll 75 | void 'Shortening expressions [when-then with otherwise]'() { 76 | when: 'There are only when-then expressions' 77 | // tag::whereWithNoWhere[] 78 | Maybe result = check(weight: value) { 79 | when { weight <= 51 } then { "You're underweight cause you weight $weight" } 80 | when { weight <= 90 } then { "You're fat" } 81 | otherwise { "Default" } 82 | } 83 | // end::whereWithNoWhere[] 84 | then: 'I should be able to get the rigth result' 85 | val(result) == expected 86 | where: 'The test value is' 87 | value | expected 88 | 51 | "You're underweight cause you weight 51" 89 | 90 | "You're fat" 90 | 200 | "Default" 91 | } 92 | 93 | @Unroll 94 | void 'Build switch-case like statements (Classes)'() { 95 | given: 'An expression' 96 | // tag::whereSwitchLike1[] 97 | Maybe result = check(value) { 98 | when String then { stringMessage } 99 | when Integer then { bigIntegerMessage } 100 | otherwise { otherwiseMessage } 101 | where { 102 | stringMessage = "Is a String" 103 | bigIntegerMessage = "Is a Integer" 104 | otherwiseMessage = "No Idea" 105 | } 106 | } 107 | // end::whereSwitchLike1[] 108 | expect: "The underweight output" 109 | result.isPresent() == true 110 | val(result) == expected 111 | where: 112 | value | expected 113 | "hi" | "Is a String" 114 | 70 | "Is a Integer" 115 | null | "No Idea" 116 | } 117 | 118 | @Unroll 119 | void 'Build switch-case like statements (Ranges)'() { 120 | given: 'An expression' 121 | // tag::whereSwitchLike2[] 122 | Maybe result = check(value) { 123 | when 10..20 then { SMALL } 124 | when 20..30 then { MEDIUM } 125 | otherwise { BIG } 126 | where { 127 | SMALL = "Small" 128 | MEDIUM = "Medium" 129 | BIG = "No Idea" 130 | } 131 | } 132 | // end::whereSwitchLike2[] 133 | expect: "The underweight output" 134 | result.isPresent() == true 135 | val(result) == expected 136 | where: 137 | value | expected 138 | 15 | "Small" 139 | 25 | "Medium" 140 | null | "No Idea" 141 | } 142 | 143 | @Unroll 144 | void 'Build switch-case like statements (Number)'() { 145 | given: 'An expression' 146 | // tag::whereSwitchLike3[] 147 | Maybe result = check(value) { 148 | when 15 then { SMALL } 149 | when 25 then { MEDIUM } 150 | otherwise { BIG } 151 | where { 152 | SMALL = "Small" 153 | MEDIUM = "Medium" 154 | BIG = "No Idea" 155 | } 156 | } 157 | // end::whereSwitchLike3[] 158 | expect: "The underweight output" 159 | result.isPresent() == true 160 | val(result) == expected 161 | where: 162 | value | expected 163 | 15 | "Small" 164 | 25 | "Medium" 165 | null | "No Idea" 166 | } 167 | 168 | } 169 | -------------------------------------------------------------------------------- /fnz-site/src/site/theme/stylesheets/stylish-portfolio.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Start Bootstrap - Stylish Portfolio Bootstrap Theme (http://startbootstrap.com) 3 | * Code licensed under the Apache License v2.0. 4 | * For details, see http://www.apache.org/licenses/LICENSE-2.0. 5 | */ 6 | 7 | /* Global Styles */ 8 | 9 | html, 10 | body { 11 | width: 100%; 12 | height: 100%; 13 | font-size: 105%; 14 | } 15 | 16 | pre { font-size: 90%; margin-top: 15px; margin-botton:15px;} 17 | 18 | body { 19 | font-family: "Source Sans Pro","Helvetica Neue",Helvetica,Arial,sans-serif; 20 | } 21 | 22 | .fa { 23 | color: #324760; 24 | } 25 | 26 | .fa-circle { 27 | color: white; 28 | } 29 | 30 | .text-vertical-center { 31 | display: table-cell; 32 | text-align: center; 33 | vertical-align: middle; 34 | } 35 | 36 | .text-vertical-center h1 { 37 | margin: 0; 38 | padding: 0; 39 | font-size: 4.5em; 40 | font-weight: 700; 41 | color: #FFF; 42 | } 43 | 44 | .text-vertical-center h3 { 45 | color: #FFF; 46 | } 47 | 48 | .bg-primary { 49 | color: #fff; 50 | background: url('../images/bg.jpg') repeat; 51 | } 52 | /* Custom Button Styles */ 53 | 54 | .btn-dark { 55 | border-radius: 0; 56 | color: #fff; 57 | background-color: rgba(0,0,0,0.4); 58 | } 59 | 60 | .btn-dark:hover, 61 | .btn-dark:focus, 62 | .btn-dark:active { 63 | color: #fff; 64 | background-color: rgba(0,0,0,0.7); 65 | } 66 | 67 | .btn-light { 68 | border-radius: 0; 69 | color: #333; 70 | background-color: rgb(255,255,255); 71 | } 72 | 73 | .btn-light:hover, 74 | .btn-light:focus, 75 | .btn-light:active { 76 | color: #333; 77 | background-color: rgba(255,255,255,0.8); 78 | } 79 | 80 | /* Custom Horizontal Rule */ 81 | 82 | hr.small { 83 | max-width: 100px; 84 | } 85 | 86 | /* Side Menu */ 87 | 88 | #sidebar-wrapper { 89 | z-index: 1000; 90 | position: fixed; 91 | right: 0; 92 | width: 250px; 93 | height: 100%; 94 | margin-right: -250px; 95 | overflow-y: auto; 96 | background: #222; 97 | -webkit-transition: all 0.4s ease 0s; 98 | -moz-transition: all 0.4s ease 0s; 99 | -ms-transition: all 0.4s ease 0s; 100 | -o-transition: all 0.4s ease 0s; 101 | transition: all 0.4s ease 0s; 102 | } 103 | 104 | .sidebar-nav { 105 | position: absolute; 106 | top: 0; 107 | width: 250px; 108 | margin: 0; 109 | padding: 0; 110 | list-style: none; 111 | } 112 | 113 | .sidebar-nav li { 114 | text-indent: 20px; 115 | line-height: 40px; 116 | } 117 | 118 | .sidebar-nav li a { 119 | display: block; 120 | text-decoration: none; 121 | color: #999; 122 | } 123 | 124 | .sidebar-nav li a:hover { 125 | text-decoration: none; 126 | color: #fff; 127 | background: rgba(255,255,255,0.2); 128 | } 129 | 130 | .sidebar-nav li a:active, 131 | .sidebar-nav li a:focus { 132 | text-decoration: none; 133 | } 134 | 135 | .sidebar-nav > .sidebar-brand { 136 | height: 55px; 137 | font-size: 18px; 138 | line-height: 55px; 139 | } 140 | 141 | .sidebar-nav > .sidebar-brand a { 142 | color: #999; 143 | } 144 | 145 | .sidebar-nav > .sidebar-brand a:hover { 146 | color: #fff; 147 | background: none; 148 | } 149 | 150 | #menu-toggle { 151 | z-index: 1; 152 | position: fixed; 153 | top: 0; 154 | right: 0; 155 | } 156 | 157 | #sidebar-wrapper.active { 158 | right: 250px; 159 | width: 250px; 160 | -webkit-transition: all 0.4s ease 0s; 161 | -moz-transition: all 0.4s ease 0s; 162 | -ms-transition: all 0.4s ease 0s; 163 | -o-transition: all 0.4s ease 0s; 164 | transition: all 0.4s ease 0s; 165 | } 166 | 167 | .toggle { 168 | margin: 5px 5px 0 0; 169 | } 170 | 171 | /* Header */ 172 | 173 | .header { 174 | display: table; 175 | position: relative; 176 | width: 100%; 177 | height: 100%; 178 | background: url(../images/bg.jpg) no-repeat center center scroll; 179 | -webkit-background-size: cover; 180 | -moz-background-size: cover; 181 | background-size: cover; 182 | -o-background-size: cover; 183 | } 184 | 185 | /* About */ 186 | 187 | .about { 188 | padding: 50px 0; 189 | } 190 | 191 | /* Services */ 192 | 193 | .services { 194 | padding: 50px 0; 195 | } 196 | 197 | .service-item { 198 | margin-bottom: 30px; 199 | } 200 | 201 | /* Callout */ 202 | 203 | .callout { 204 | display: table; 205 | width: 100%; 206 | height: 400px; 207 | color: #fff; 208 | background: url(../images/callout.jpg) no-repeat center center scroll; 209 | -webkit-background-size: cover; 210 | -moz-background-size: cover; 211 | background-size: cover; 212 | -o-background-size: cover; 213 | } 214 | 215 | /* Portfolio */ 216 | 217 | .portfolio { 218 | padding: 50px 0; 219 | } 220 | 221 | .portfolio-item { 222 | margin-bottom: 30px; 223 | } 224 | 225 | .img-portfolio { 226 | margin: 0 auto; 227 | } 228 | 229 | .img-portfolio:hover { 230 | opacity: 0.8; 231 | } 232 | 233 | /* Call to Action */ 234 | 235 | .call-to-action { 236 | padding: 50px 0; 237 | } 238 | 239 | .call-to-action .btn { 240 | margin: 10px; 241 | } 242 | 243 | /* Map */ 244 | 245 | .map { 246 | height: 500px; 247 | } 248 | 249 | @media(max-width:768px) { 250 | .map { 251 | height: 75%; 252 | } 253 | } 254 | 255 | /* Footer */ 256 | 257 | footer { 258 | padding: 100px 0; 259 | } 260 | /*Modals*/ 261 | 262 | .portfolio-modal .modal-content { 263 | padding: 100px 0; 264 | min-height: 100%; 265 | border: 0; 266 | border-radius: 0; 267 | text-align: center; 268 | background-clip: border-box; 269 | -webkit-box-shadow: none; 270 | box-shadow: none; 271 | } 272 | 273 | .portfolio-modal .modal-content h2 { 274 | margin: 0; 275 | font-size: 3em; 276 | } 277 | 278 | .portfolio-modal .modal-content img { 279 | margin-bottom: 30px; 280 | } 281 | 282 | .portfolio-modal .modal-content .item-details { 283 | margin: 30px 0; 284 | } 285 | 286 | .portfolio-modal .close-modal { 287 | position: absolute; 288 | top: 25px; 289 | right: 25px; 290 | width: 75px; 291 | height: 75px; 292 | background-color: transparent; 293 | cursor: pointer; 294 | } 295 | 296 | .portfolio-modal .close-modal:hover { 297 | opacity: .3; 298 | } 299 | 300 | .portfolio-modal .close-modal .lr { 301 | z-index: 1051; 302 | width: 1px; 303 | height: 75px; 304 | margin-left: 35px; 305 | background-color: #2c3e50; 306 | -webkit-transform: rotate(45deg); 307 | -ms-transform: rotate(45deg); 308 | transform: rotate(45deg); 309 | } 310 | 311 | .portfolio-modal .close-modal .lr .rl { 312 | z-index: 1052; 313 | width: 1px; 314 | height: 75px; 315 | background-color: #2c3e50; 316 | -webkit-transform: rotate(90deg); 317 | -ms-transform: rotate(90deg); 318 | transform: rotate(90deg); 319 | } 320 | 321 | .img-centered { 322 | margin: 0 auto; 323 | } 324 | 325 | .code { 326 | color: white; 327 | background: #324760; 328 | } 329 | -------------------------------------------------------------------------------- /fnz-site/src/docs/control.ad: -------------------------------------------------------------------------------- 1 | == Control Flow 2 | 3 | [, Wikipedia] 4 | "" 5 | In computer science, control flow (or alternatively, flow of control) refers to the order in which 6 | the individual statements, instructions or function calls of an imperative or a declarative program 7 | are executed or evaluated 8 | "" 9 | 10 | Control flow is one of the most important topics in a programming language. Most languages 11 | have their own out-of-the-box control structures, and some others can create new ones on-the-fly. 12 | Although Groovy can't create new control structures in the classical sense, it can create control 13 | structures based on functions and meta-programming. 14 | 15 | Following up there're some control flow structures I've been collecting while sneaking 16 | in some other programming languages. 17 | 18 | Let's go through them. 19 | 20 | === Let 21 | 22 | Initializes a group of variables, makes them available within the scope 23 | of the closure passed as argument. 24 | 25 | [source,groovy] 26 | ---- 27 | let(Map,Closure) 28 | ---- 29 | 30 | Let's see some examples. 31 | 32 | [source,groovy] 33 | .Simple values 34 | ---- 35 | include::{testdir}/fnz/ast/flow/LetSpecExample.groovy[tags=simpleLet] 36 | ---- 37 | 38 | Here values provided by the map are accessible within the scope of the closure block. 39 | What if I wanted to create values depending on previous values ? Let's 40 | see the following example: 41 | 42 | [source,groovy] 43 | .Dependant expressions 44 | ---- 45 | include::{testdir}/fnz/ast/flow/LetSpecExample.groovy[tags=computedValues] 46 | ---- 47 | 48 | In this example value of *'z'* depends on the values given to *'x'* and *'y'*. Every 49 | 50 | [source,groovy] 51 | .Nested expressions 52 | ---- 53 | include::{testdir}/fnz/ast/flow/LetSpecExample.groovy[tags=nestedLet] 54 | ---- 55 | 56 | Previous link:https://code.google.com/p/spock[Spock] specification shows how let expressions could be nested. 57 | 58 | === Letm 59 | 60 | `letm` works the same way `let` does but instead of handling plain 61 | values it works with monadic values and eventually will return an 62 | instance of type `fnz.data.Monad`. Why is that ? 63 | 64 | Well `letm` is meant to work with monad expressions. So when you're 65 | seeing this: 66 | 67 | [source, groovy] 68 | ---- 69 | letm(x: Just(1), y: { Just(x+1) } ) { 70 | Just(y) 71 | } 72 | ---- 73 | 74 | Under the hood the expression will be transformed to: 75 | 76 | [source, groovy] 77 | ---- 78 | bind(Just(1)) { x -> 79 | bind({ Just(x+1) }())) { y -> 80 | Just(y) 81 | } 82 | } 83 | ---- 84 | 85 | This way you can play with other flavors of monads. Imagine you want 86 | to combine let with the `Either` monad. 87 | 88 | [source, groovy] 89 | ---- 90 | include::{testdir}/fnz/ast/flow/LetmSpecExample.groovy[tags=workingWithOtherMonads] 91 | ---- 92 | 93 | You can also combine it with the `Try` monad. This time there's a 94 | method you want to use, but it could throw some exceptions, you want 95 | to handle that possibility using `Try`. 96 | 97 | [source, groovy] 98 | ---- 99 | include::{testdir}/fnz/ast/flow/LetmSpecExample.groovy[tags=combineWithTry] 100 | ---- 101 | 102 | === Do 103 | 104 | If you have seen Haskell's *do* expressions, they look like this: 105 | 106 | [source, haskell] 107 | ---- 108 | do 109 | x <- Just 1 110 | y <- Just (x + 1) 111 | return x 112 | ---- 113 | 114 | There are a couple of nice things when using this control flow: 115 | 116 | * You don't have to unwrapped/wrap monadic values. In the example when 117 | assigning *Just 1* to *x* the value inside (1) it's unwrapped and make it 118 | available for the next expression to use it. 119 | * In the end using the *return* expression make it easier for the programmer 120 | to wrap the final value in the proper monadic value without having to do it 121 | explicitly. It is assumed by the expression flow. 122 | 123 | Well, *Fnz* has tried to mimic the behavior of this expression using both 124 | methods *$do* and *$return*: 125 | 126 | [source, groovy] 127 | ---- 128 | include::{testdir}/fnz/ast/flow/DoSpec.groovy[tags=simpleExample] 129 | ---- 130 | 131 | Although in Haskell *do* notation you can be using assignments as long 132 | as other types of expressions, here is not possible. Only assignment 133 | expressions are allowed by design. 134 | 135 | So you may be wondering... How can I use an expression not returning 136 | any value within the $do/$return statement ? 137 | 138 | Well for that purpose a special rule has been added to the *do* notation 139 | expression. Whenever you wan to use an expression not returning anything 140 | you should assign that expression to *_*. 141 | 142 | The *_* variable name means that you don't care about the returning 143 | type. In fact every time you use that name a unique identifier is 144 | created underneath. 145 | 146 | [source, groovy] 147 | ---- 148 | include::{testdir}/fnz/ast/flow/DoSpec.groovy[tags=wildcard1] 149 | ---- 150 | 151 | === Unless 152 | 153 | The rationale behind *Unless* is that sometimes is just a pain to create *negative* 154 | conditional expressions. 155 | 156 | [source,groovy] 157 | ---- 158 | unless(Boolean,Closure) 159 | ---- 160 | 161 | [source,groovy] 162 | .Unless structure 163 | ---- 164 | include::{testdir}/fnz/ast/flow/UnlessSpecExample.groovy[tags=simpleUnless] 165 | ---- 166 | 167 | This expression will allow the code block to be executed unless the condition 168 | evaluates to true. 169 | 170 | === Where 171 | 172 | I would say *Where* control belongs to the family of *switch statements* or *multiway branches*. 173 | I first read about the *Where* clause while I was reading Haskell. 174 | 175 | This version of Haskell's Where expressions has: 176 | 177 | [source,groovy] 178 | ---- 179 | check(Map) { // <1> 180 | when { /evaluates to boolean/ } then { /returns a value/ } // <2> 181 | otherwise { /returns a value/ } // <3> 182 | where { /some constants/ } // <4> 183 | } 184 | ---- 185 | 186 | <1> A *check* method is the entry point to this expression 187 | <2> *When-Then* are evaluated in two different phases. First all when clauses are evaluated 188 | until one of them is evaluated to true. Then the related then clause is evaluated. 189 | <3> The *otherwise* block is executed only if no when-then block has been evaluated successfully 190 | before. 191 | <4> The *where* block is a perfect place to initializate constants used in any of the other mentioned 192 | blocks. 193 | 194 | There are different ways of using this control depending on the use 195 | of when-then expressions 196 | 197 | [source,groovy] 198 | .Simple Where 199 | ---- 200 | include::{testdir}/fnz/flow/WhereSpec.groovy[tags=simpleWhere] 201 | ---- 202 | 203 | This is a simple example about how to use the where expression. You can also omit 204 | some of the parts mentioned before 205 | 206 | [source,groovy] 207 | .Ommiting Where 208 | ---- 209 | include::{testdir}/fnz/flow/WhereSpec.groovy[tags=whereWithNoWhere] 210 | ---- 211 | 212 | [source,groovy] 213 | .Only When-Then clauses 214 | ---- 215 | include::{testdir}/fnz/flow/WhereSpec.groovy[tags=whereOnlyWithWhenThen] 216 | ---- 217 | 218 | The when-then clauses use closures to evaluate their content. 219 | But thanks to Groovy's objects method isCase() we can create switch-like statements 220 | 221 | [source,groovy] 222 | .Switch-case Where (Class) 223 | ---- 224 | include::{testdir}/fnz/flow/WhereSpec.groovy[tags=whereSwitchLike1] 225 | ---- 226 | 227 | Instead of passing a map you can evaluate a single value against some conditions. Those 228 | conditions are the same you would be using in a switch-case block in Groovy. 229 | 230 | [source,groovy] 231 | .Switch-case Where (Collections) 232 | ---- 233 | include::{testdir}/fnz/flow/WhereSpec.groovy[tags=whereSwitchLike2] 234 | ---- 235 | 236 | [source,groovy] 237 | .Switch-case Where (Number) 238 | ---- 239 | include::{testdir}/fnz/flow/WhereSpec.groovy[tags=whereSwitchLike3] 240 | ---- 241 | 242 | -------------------------------------------------------------------------------- /fnz-core/src/test/groovy/fnz/data/EitherSpec.groovy: -------------------------------------------------------------------------------- 1 | package fnz.data 2 | 3 | import static Either.right 4 | import static Either.left 5 | 6 | import static fnz.Fnz.Either 7 | import static fnz.Fnz.Right 8 | import static fnz.Fnz.val 9 | 10 | import fnz.Fnz 11 | import spock.lang.Specification 12 | 13 | class EitherSpec extends Specification { 14 | 15 | void 'Applicative: Either applicative implementation'() { 16 | expect: 17 | left(null).fapply(right(1)).typedRef.value == null 18 | } 19 | 20 | void 'applicative: Either applicative not null'() { 21 | given: 'there is a valid function' 22 | Function inc = { x -> x + 1 } 23 | expect: 'the right value to pop up' 24 | val(right(1).fapply(right(inc))) == 2 25 | } 26 | 27 | // tag::functor2[] 28 | void 'Either functor implementation'() { 29 | given: 'a function we want to apply' 30 | def inc = { Integer v -> v + 1 } 31 | expect: 'to return the result of applying the function when RIGHT' 32 | right(1).fmap(inc).typedRef.value == 2 33 | right(2).fmap(inc).typedRef.value == 3 34 | and: 'to return the same input when LEFT' 35 | left(null).fmap(inc).typedRef.value == null 36 | } 37 | // end::functor2[] 38 | 39 | // tag::functor3[] 40 | void 'first law'() { 41 | when: 'mapping identity function overy every item in a container' 42 | def identity = { Integer v -> v } 43 | then: 'it has no effect' 44 | right(1).fmap(identity).typedRef.value == 1 45 | } 46 | // end::functor3[] 47 | 48 | // tag::functor4[] 49 | void 'second law'() { 50 | when: 'Mapping a composition of two functions over every item in a container' 51 | def inc = { Integer v -> v + 1 } 52 | def twoTimes = { Integer v -> v * 2 } 53 | def composition = inc >> twoTimes 54 | then: 'the same as first mapping one function' 55 | and: 'then mapping the other' 56 | right(1) 57 | .fmap(composition) 58 | .typedRef.value == right(right(1).fmap(inc).typedRef.value) 59 | .fmap(twoTimes) 60 | .typedRef.value 61 | } 62 | // end::functor4[] 63 | 64 | // tag::eithermonad1[] 65 | void 'Either monad'() { 66 | when: 'having a fmap function' 67 | def inc = { Integer v -> v + 1 } 68 | and: 'the monad bind function' 69 | def mfn = { x -> Either.right(inc(x)) } 70 | then: 'when applying to a right the monad will progress' 71 | right(1).bind(mfn).typedRef.value == 2 72 | and: 'when applying to a left it wont go any further' 73 | left(1).bind(mfn).typedRef.value == 1 74 | } 75 | // end::eithermonad1[] 76 | 77 | void 'Either monad when some function returns a left value'() { 78 | when: 'having a function that could return a left value' 79 | def div = { Integer v -> 80 | return v == 0 ? Either.left(v) : Either.right(1 / v) 81 | } 82 | then: 'if apply a valid value then the function will be applied' 83 | right(1).bind(div).typedRef.value == 1 84 | and: 'otherwise if using 0 I will get a left zero' 85 | with(left(0).bind(div)) { 86 | typedRef.value == 0 87 | } 88 | } 89 | 90 | // tag::eithermonadsearch[] 91 | void 'using Either to chain failback searchs'() { 92 | given:'a base function to search by a certain criteria' 93 | def baseSearch = { Closure search -> // <1> 94 | return { List sample -> 95 | def pr = sample.find(search) 96 | // if found then return left to shortcircuit further 97 | // searchs 98 | Fnz.Either(pr).isLeft() ? Either.right(sample) : Either.left(pr) 99 | } 100 | } 101 | and: 'composed criterias on top of the base function' 102 | // they become Function> 103 | def lookByNameJohn = baseSearch { it.name == 'john' } // <2> 104 | def lookByAgeGreaterThan = baseSearch { it.age > 50 } 105 | def lookByCity = baseSearch { it.city == 'dublin' } 106 | when: 'chaining all search criterias' 107 | Either result = // <3> 108 | Either.right(sample) 109 | .bind(lookByNameJohn) 110 | .bind(lookByAgeGreaterThan) 111 | .bind(lookByCity) 112 | then: 'there should be only one item' 113 | result.isLeft() 114 | !result.isRight() 115 | result.typedRef.value.name == name_of_the_result 116 | where: 'samples used in this spec are' 117 | sample | name_of_the_result 118 | firstSample | 'john' 119 | secondSample | 'peter' 120 | thirdSample | 'rob' 121 | } 122 | // end::eithermonadsearch[] 123 | 124 | List getFirstSample() { 125 | return [ 126 | [name:'john', age:32, city:'barcelona'], 127 | [name:'peter', age:51, city:'london'], 128 | [name:'rob', age:32, city:'dublin'] 129 | ] 130 | } 131 | 132 | List getSecondSample() { 133 | return [ 134 | [name:'peter', age:51, city:'london'], 135 | [name:'rob', age:32, city:'dublin'], 136 | [name:'johnny', age:32, city:'barcelona'] 137 | ] 138 | } 139 | 140 | List getThirdSample() { 141 | return [ 142 | [name:'rob', age:32, city:'dublin'], 143 | [name:'johnny', age:32, city:'barcelona'], 144 | [name:'peter', age:50, city:'london'] 145 | ] 146 | } 147 | 148 | // tag::eitherorvalue[] 149 | void 'using OR as an alternative value'() { 150 | when: 'a non valid expression' 151 | Either possible = Either(value) | Right(0) 152 | then: 'we should get what we expected' 153 | val(possible) == expected 154 | and: 'eventually it will be right' 155 | possible.isRight() 156 | where: 'possible values are' 157 | value | expected 158 | null | 0 159 | 1 | 1 160 | } 161 | // end::eitherorvalue[] 162 | 163 | // tag::eitherorcomputation[] 164 | void 'using OR for a lazy computation alternative'() { 165 | when: 'a non valid expression' 166 | Either possible = Either(value) | { Right(0) } 167 | then: 'we should get what we expected' 168 | val(possible) == expected 169 | and: 'rules must apply' 170 | possible.isRight() 171 | where: 'possible values are' 172 | value | expected 173 | null | 0 174 | 1 | 1 175 | } 176 | // end::eitherorcomputation[] 177 | 178 | void 'testing bind with type awareness and unit'() { 179 | when: 'using a valid expression' 180 | TypeAwareFunction> fn = { clazz, value -> 181 | return clazz.unit(value + 1) 182 | } 183 | Either possible = options.bind2(fn) 184 | then: 'the result should have the type' 185 | possible instanceof Either 186 | and: 'we should get the expected value' 187 | val(possible) == expected 188 | where: 'possible options are' 189 | options | expected 190 | right(1) | 2 191 | left(1) | 1 192 | } 193 | 194 | void 'using boolean representation'() { 195 | given: 'a list of possible values' 196 | List values = [Fnz.Left(1), Fnz.Right(2)] 197 | when: 'filtering the list' 198 | def result = values.findAll()*.get() 199 | then: 'the only valid result is 2' 200 | result == [2] 201 | } 202 | 203 | } 204 | -------------------------------------------------------------------------------- /fnz-core/src/main/groovy/fnz/ast/type/FuncAstTransformer.groovy: -------------------------------------------------------------------------------- 1 | package fnz.ast.type 2 | 3 | import static org.codehaus.groovy.ast.ClassHelper.make 4 | import static org.codehaus.groovy.ast.tools.GeneralUtils.constX 5 | import static org.codehaus.groovy.ast.tools.GeneralUtils.param 6 | import static org.codehaus.groovy.ast.tools.GeneralUtils.params 7 | 8 | import static fnz.ast.AstUtils.getArgs 9 | import static fnz.ast.AstUtils.isBinaryExpression 10 | import static fnz.ast.AstUtils.getUniqueIdentifier 11 | import static fnz.ast.AstUtils.isToken 12 | 13 | import static org.codehaus.groovy.syntax.Types.COMPARE_GREATER_THAN_EQUAL 14 | 15 | import fnz.ast.MethodCallExpressionTransformer 16 | 17 | import groovy.transform.CompileDynamic 18 | import groovy.transform.CompileStatic 19 | 20 | import org.codehaus.groovy.ast.MixinNode 21 | import org.codehaus.groovy.ast.ClassNode 22 | import org.codehaus.groovy.ast.InnerClassNode 23 | import org.codehaus.groovy.ast.MethodNode 24 | import org.codehaus.groovy.ast.Parameter 25 | import org.codehaus.groovy.ast.GenericsType 26 | 27 | import org.codehaus.groovy.ast.expr.Expression 28 | import org.codehaus.groovy.ast.expr.ListExpression 29 | import org.codehaus.groovy.ast.expr.VariableExpression 30 | import org.codehaus.groovy.ast.expr.MethodCallExpression 31 | import org.codehaus.groovy.ast.expr.ArgumentListExpression 32 | import org.codehaus.groovy.ast.expr.BinaryExpression 33 | 34 | import org.codehaus.groovy.syntax.Token 35 | import org.codehaus.groovy.control.SourceUnit 36 | 37 | @CompileStatic 38 | class FuncAstTransformer extends MethodCallExpressionTransformer { 39 | 40 | static final String TYPE_METHOD_NAME = 'func' 41 | static final String FI_FUNCTION_NAME = 'apply' 42 | static final String FI_FUNCTION_PARAM_NAME = 'input' 43 | static final Expression MEANING_OF_LIFE = constX(42) 44 | 45 | FuncAstTransformer(SourceUnit sourceUnit) { 46 | super(sourceUnit, TYPE_METHOD_NAME) 47 | } 48 | 49 | Expression transformMethodCall(final MethodCallExpression methodCallExpression) { 50 | Expression firstArg = getArgs(methodCallExpression).first() 51 | Boolean isValid = checkIsBinaryExpressionWithToken(firstArg, COMPARE_GREATER_THAN_EQUAL) 52 | 53 | if (!isValid) return 54 | 55 | InnerClassNode innerClassNode = getFunctionalInterface((BinaryExpression) firstArg) 56 | module.addClass(innerClassNode) 57 | 58 | return MEANING_OF_LIFE 59 | } 60 | 61 | @CompileDynamic 62 | Boolean checkIsBinaryExpressionWithToken(Expression expression, int tokenReference) { 63 | if (!isBinaryExpression(expression)) { 64 | addError(expression, "Expected binary expression here. Something like: Fn(A) >> String >> A") 65 | return false 66 | } 67 | 68 | if (!isToken(expression.operation, tokenReference)) { 69 | addError(expression, "Token expected $tokenReference got ${expression.operation}") 70 | return false 71 | } 72 | 73 | return true 74 | } 75 | 76 | private Boolean byMainClassName(String mainName, ClassNode classNode) { 77 | return classNode.name == mainName 78 | } 79 | 80 | @CompileDynamic 81 | private InnerClassNode getFunctionalInterface(BinaryExpression fiExpression) { 82 | Expression typeInfo = fiExpression.leftExpression 83 | Expression fnInfo = fiExpression.rightExpression 84 | 85 | InnerClassNode innerClassNode = extractInnerClass(typeInfo) 86 | MethodNode abstractMethod = extractMethod(fnInfo) 87 | 88 | innerClassNode.addMethod(abstractMethod) 89 | 90 | return innerClassNode 91 | } 92 | 93 | private InnerClassNode extractInnerClass(MethodCallExpression methodCallExpression) { 94 | String innerClassName = "${modulePackageName}${methodCallExpression.methodAsString}" 95 | InnerClassNode innerClassNode = getInnerClass(innerClassName) 96 | 97 | innerClassNode.setGenericsTypes(extractGenericsFrom(methodCallExpression)) 98 | innerClassNode.genericsPlaceHolder = true 99 | 100 | return innerClassNode 101 | } 102 | 103 | private InnerClassNode extractInnerClass(VariableExpression typeInfoExpression) { 104 | return getInnerClass("${modulePackageName}${typeInfoExpression.name}") 105 | } 106 | 107 | private InnerClassNode getInnerClass(String innerClassName) { 108 | Closure search = this.&byMainClassName.curry(module.mainClassName) 109 | ClassNode outerClassNode = module.classes.find(search) 110 | 111 | InnerClassNode innerClassNode = 112 | new InnerClassNode( 113 | outerClassNode, 114 | innerClassName, 115 | innerClassModifiers, 116 | make(Object), 117 | [] as ClassNode[], 118 | [] as MixinNode[] 119 | ) 120 | 121 | return innerClassNode 122 | } 123 | 124 | private int getInnerClassModifiers() { 125 | return ACC_STATIC | ACC_ABSTRACT | ACC_INTERFACE 126 | } 127 | 128 | @CompileDynamic 129 | private MethodNode extractMethod(BinaryExpression fnExpression) { 130 | Expression inputExpression = fnExpression.leftExpression 131 | Expression outputExpression = fnExpression.rightExpression 132 | 133 | ClassNode returnType = extractReturnType(outputExpression) 134 | Parameter[] parameters = extractParametersFrom(inputExpression) 135 | ClassNode[] exceptions = [] as ClassNode[] 136 | 137 | MethodNode methodNode = 138 | new MethodNode( 139 | FI_FUNCTION_NAME, 140 | ACC_PUBLIC | ACC_ABSTRACT, 141 | returnType, 142 | parameters, 143 | exceptions, 144 | null 145 | ) 146 | 147 | return methodNode 148 | } 149 | 150 | private ClassNode extractReturnType(MethodCallExpression methodCallExpression) { 151 | return extractTypeFrom(methodCallExpression) 152 | } 153 | 154 | private ClassNode extractReturnType(VariableExpression variableExpression) { 155 | return make(variableExpression.name) 156 | } 157 | 158 | private Parameter[] extractParametersFrom(MethodCallExpression methodCallExpression) { 159 | return params(getParameterFrom(methodCallExpression)) 160 | } 161 | 162 | private Parameter[] extractParametersFrom(VariableExpression variableExpression) { 163 | return params(getParameterFrom(variableExpression)) 164 | } 165 | 166 | private Parameter[] extractParametersFrom(ListExpression listExpression) { 167 | return listExpression.expressions.collect(this.&getParameterFrom) as Parameter[] 168 | } 169 | 170 | private Parameter getParameterFrom(VariableExpression variable) { 171 | return param(make(variable.name), getUniqueIdentifier()) 172 | } 173 | 174 | private Parameter getParameterFrom(MethodCallExpression methodCallExpression) { 175 | return param(extractTypeFrom(methodCallExpression), getUniqueIdentifier()) 176 | } 177 | 178 | private ClassNode extractTypeFrom(MethodCallExpression methodCallExpression) { 179 | String principalClassName = methodCallExpression.methodAsString 180 | 181 | ClassNode classNode = make(principalClassName) 182 | classNode.genericsPlaceHolder = true 183 | classNode.genericsTypes = extractGenericsFrom(methodCallExpression) 184 | 185 | return classNode 186 | } 187 | 188 | private GenericsType[] extractGenericsFrom(MethodCallExpression methodCallExpression) { 189 | ArgumentListExpression args = ((ArgumentListExpression) methodCallExpression.arguments) 190 | List genericTypes = args.expressions 191 | GenericsType[] genericsArray = 192 | genericTypes.collect(this.&extractGenericsTypeFrom) as GenericsType[] 193 | 194 | return genericsArray 195 | } 196 | 197 | private GenericsType extractGenericsTypeFrom(VariableExpression variableExpression) { 198 | return new GenericsType(make(variableExpression.name)) 199 | } 200 | 201 | } 202 | --------------------------------------------------------------------------------