diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..f354bb9 --- /dev/null +++ b/.gitignore @@ -0,0 +1,165 @@ +# Created by https://www.toptal.com/developers/gitignore/api/jetbrains,go,vim +# Edit at https://www.toptal.com/developers/gitignore?templates=jetbrains,go,vim + +### Go ### +# If you prefer the allow list template instead of the deny list, see community template: +# https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore +# +# Binaries for programs and plugins +*.exe +*.exe~ +*.dll +*.so +*.dylib + +# Test binary, built with `go test -c` +*.test + +# Output of the go coverage tool, specifically when used with LiteIDE +*.out + +# Dependency directories (remove the comment below to include it) +# vendor/ + +# Go workspace file +go.work + +### Go Patch ### +/vendor/ +/Godeps/ + +### JetBrains ### +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# AWS User-specific +.idea/**/aws.xml + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/jarRepositories.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# SonarLint plugin +.idea/sonarlint/ + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +### JetBrains Patch ### +# Comment Reason: https://github.com/joeblau/gitignore.io/issues/186#issuecomment-215987721 + +# *.iml +# modules.xml +# .idea/misc.xml +# *.ipr + +# Sonarlint plugin +# https://plugins.jetbrains.com/plugin/7973-sonarlint +.idea/**/sonarlint/ + +# SonarQube Plugin +# https://plugins.jetbrains.com/plugin/7238-sonarqube-community-plugin +.idea/**/sonarIssues.xml + +# Markdown Navigator plugin +# https://plugins.jetbrains.com/plugin/7896-markdown-navigator-enhanced +.idea/**/markdown-navigator.xml +.idea/**/markdown-navigator-enh.xml +.idea/**/markdown-navigator/ + +# Cache file creation bug +# See https://youtrack.jetbrains.com/issue/JBR-2257 +.idea/$CACHE_FILE$ + +# CodeStream plugin +# https://plugins.jetbrains.com/plugin/12206-codestream +.idea/codestream.xml + +# Azure Toolkit for IntelliJ plugin +# https://plugins.jetbrains.com/plugin/8053-azure-toolkit-for-intellij +.idea/**/azureSettings.xml + +### Vim ### +# Swap +[._]*.s[a-v][a-z] +!*.svg # comment out if you don't need vector files +[._]*.sw[a-p] +[._]s[a-rt-v][a-z] +[._]ss[a-gi-z] +[._]sw[a-p] + +# Session +Session.vim +Sessionx.vim + +# Temporary +.netrwhist +*~ +# Auto-generated tag files +tags +# Persistent undo +[._]*.un~ + +# End of https://www.toptal.com/developers/gitignore/api/jetbrains,go,vim \ No newline at end of file diff --git a/README.md b/README.md index 0a7deb5..731a3a6 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,12 @@ [![GitHub go.mod Go version of a Go module](https://img.shields.io/github/go-mod/go-version/medmouine/gomad.svg)](https://github.com/medmouine/gomad) [![Test](https://github.com/medmouine/gomad/workflows/Test/badge.svg)](https://github.com/medmouine/gomad/actions/workflows/test.yml) -[![Lint](https://github.com/medmouine/gomad/workflows/Lint/badge.svg)](https://github.com/medmouine/gomad/actions/workflows/lint.yml) +[![TLint](https://github.com/medmouine/gomad/workflows/Lint/badge.svg)](https://github.com/medmouine/gomad/actions/workflows/lint.yml) [![codecov](https://codecov.io/gh/medmouine/gomad/branch/main/graph/badge.svg?token=3DJBNCU1NG)](https://codecov.io/gh/medmouine/gomad) [![Maintainability](https://api.codeclimate.com/v1/badges/1eaa76225406955456d1/maintainability)](https://codeclimate.com/github/medmouine/gomad/maintainability) [![Total alerts](https://img.shields.io/lgtm/alerts/g/medmouine/gomad.svg?logo=lgtm&logoWidth=18)](https://lgtm.com/projects/g/medmouine/gomad/alerts/) -[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +[![TLicense](https://img.shields.io/badge/License-Apache_2.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) drawing diff --git a/apply/apply.go b/apply/apply.go index 743a9c2..e8dcd40 100644 --- a/apply/apply.go +++ b/apply/apply.go @@ -1 +1,5 @@ package apply + +type IApply[T any] interface { + Ap() +} diff --git a/either/either.go b/either/either.go index 1871089..509329e 100644 --- a/either/either.go +++ b/either/either.go @@ -5,24 +5,23 @@ import "github.com/medmouine/gomad/maybe" /* Either allows to manipulate pairs of mutually exclusive data. For example, if we would want to fall back to a value B if A answers to a specific predicate. -This interface allows integrating this behavior seamlessly by abstracting all the underlying logic of managing both values. +This interface allows integrating this behavior seamlessly by abstracting all the underlying logic of +managing both values. A common use case for this is form validation for front-end applications. */ -type Either[L any, R any] interface { +type Either[L, R any] interface { IsRight() bool - MapRight(func(R) R) Either[L, R] - Right() R - RightOr(R) R - RightOrElse(func() R) R + Right() *R + RightOr(R) *R + RightOrElse(func() R) *R IfRight(func(R)) Either[L, R] IfNotRight(func()) Either[L, R] MaybeRight() maybe.Maybe[R] IsLeft() bool - MapLeft(func(L) L) Either[L, R] - Left() L - LeftOr(L) L - LeftOrElse(func() L) L + Left() *L + LeftOr(L) *L + LeftOrElse(func() L) *L IfLeft(func(L)) Either[L, R] IfNotLeft(func()) Either[L, R] MaybeLeft() maybe.Maybe[L] @@ -32,18 +31,18 @@ type Either[L any, R any] interface { type either[L any, R any] struct { Either[L, R] - left *left[L, R] - right *right[L, R] } /* -FromPredicateC returns a function that creates a new Either based on a given predicate and a Left value in case of failure of the predicate. +FromPredicateC returns a function that creates a new Either based on a given predicate and a Left value in case of +failure of the predicate. */ -func FromPredicateC[L any, R any](predicate func(R) bool, left L) func(R) Either[L, R] { +func FromPredicateC[L, R any](predicate func(R) bool, left L) func(R) Either[L, R] { return func(candidate R) Either[L, R] { if predicate(candidate) { return newR[L, R](candidate) } + return newL[L, R](left) } } @@ -52,87 +51,77 @@ func FromPredicateC[L any, R any](predicate func(R) bool, left L) func(R) Either FromPredicate returns a new Either based on a given predicate and a Left value in case of failure of the predicate and a Right value which will be tested against the predicate. */ -func FromPredicate[L any, R any](predicate func(R) bool, right R, left L) Either[L, R] { +func FromPredicate[L, R any](predicate func(R) bool, right R, left L) Either[L, R] { return FromPredicateC[L, R](predicate, left)(right) } -func (e either[L, R]) Swap() Either[R, L] { +func MapRight[R2, L, R any](e Either[L, R], f func(R) R2) Either[L, R2] { if e.IsRight() { - return newL[R, L](e.Right()) + return newR[L, R2](f(*e.Right())) } - return newR[R, L](e.Left()) -} -func (e *either[L, R]) IsLeft() bool { - return !e.IsRight() -} - -func (e *either[L, R]) MapRight(f func(R) R) Either[L, R] { - if e.IsRight() { - return newR[L, R](f(e.Right())) - } - return e + return newL[L, R2](*e.Left()) } -func (e *either[L, R]) MapLeft(f func(L) L) Either[L, R] { +func MapLeft[L2, L, R any](e Either[L, R], f func(L) L2) Either[L2, R] { if e.IsLeft() { - return newL[L, R](f(e.Left())) + return newL[L2, R](f(*e.Left())) } - return e -} -func (e either[L, R]) Left() L { - if e.IsLeft() { - return e.left.val - } - panic(any("either is not Left")) + return newR[L2, R](*e.Right()) } -func (e either[L, R]) Right() R { +func (e either[L, R]) Swap() Either[R, L] { if e.IsRight() { - return e.right.val + return newL[R, L](*e.Right()) } - panic(any("either is not Right")) + + return newR[R, L](*e.Left()) } // ################ // #### Left ##### // ################ -func (e either[L, R]) LeftOr(or L) L { +func (e either[L, R]) LeftOr(or L) *L { if e.IsLeft() { return e.Left() } - return or + + return &or } -func (e either[L, R]) LeftOrElse(orElse func() L) L { +func (e either[L, R]) LeftOrElse(orElse func() L) *L { if e.IsLeft() { return e.Left() } - return orElse() + + v := orElse() + + return &v } -func (e *either[L, R]) IfLeft(f func(L)) Either[L, R] { +func (e either[L, R]) IfLeft(f func(L)) Either[L, R] { if e.IsLeft() { - f(e.Left()) - return e + f(*e.Left()) } - return e + + return &e } -func (e *either[L, R]) IfNotLeft(f func()) Either[L, R] { +func (e either[L, R]) IfNotLeft(f func()) Either[L, R] { if !e.IsLeft() { f() - return e } - return e + + return &e } func (e either[L, R]) MaybeLeft() maybe.Maybe[L] { if e.IsLeft() { - return maybe.Just(e.Left()) + return maybe.Just(*e.Left()) } + return maybe.None[L]() } @@ -140,39 +129,44 @@ func (e either[L, R]) MaybeLeft() maybe.Maybe[L] { // #### Right ##### // ################ -func (e either[L, R]) RightOr(or R) R { +func (e either[L, R]) RightOr(or R) *R { if e.IsRight() { return e.Right() } - return or + + return &or } -func (e either[L, R]) RightOrElse(orElse func() R) R { +func (e either[L, R]) RightOrElse(orElse func() R) *R { if e.IsRight() { return e.Right() } - return orElse() + + v := orElse() + + return &v } -func (e *either[L, R]) IfRight(f func(R)) Either[L, R] { +func (e either[L, R]) IfRight(f func(R)) Either[L, R] { if e.IsRight() { - f(e.Right()) - return e + f(*e.Right()) } - return e + + return &e } -func (e *either[L, R]) IfNotRight(f func()) Either[L, R] { +func (e either[L, R]) IfNotRight(f func()) Either[L, R] { if !e.IsRight() { f() - return e } - return e + + return &e } func (e either[L, R]) MaybeRight() maybe.Maybe[R] { if e.IsRight() { - return maybe.Just(e.Right()) + return maybe.Just(*e.Right()) } + return maybe.None[R]() } diff --git a/either/either_test.go b/either/either_test.go index 07a74e4..529683b 100644 --- a/either/either_test.go +++ b/either/either_test.go @@ -1,58 +1,65 @@ -package either +package either_test import ( - "go/types" "reflect" "testing" + + . "github.com/medmouine/gomad/either" ) func TestFromPredicate(t *testing.T) { - gotRight := FromPredicate(func(i int) bool { return i%2 == 0 }, 2, "not even") + t.Parallel() + gotRight := FromPredicate(func(i int) bool { return i%2 == 0 }, 2, "not even") if gotRight.IsLeft() { t.Errorf("gotRight isLeft %v, want %v", gotRight.IsLeft(), false) } + if !gotRight.IsRight() { t.Errorf("gotRight isRight %v, want %v", gotRight.IsRight(), true) } - if !reflect.DeepEqual(gotRight.Right(), 2) { - t.Errorf("gotRight %v, want %v", gotRight.Right(), 2) + + if !reflect.DeepEqual(*gotRight.Right(), 2) { + t.Errorf("gotRight %v, want %v", *gotRight.Right(), 2) } gotLeft := FromPredicate(func(i int) bool { return i%2 == 0 }, 3, "not even") - if !gotLeft.IsLeft() { t.Errorf("gotLeft isLeft %v, want %v", gotLeft.IsLeft(), true) } + if gotLeft.IsRight() { t.Errorf("gotLeft isRight %v, want %v", gotLeft.IsRight(), false) } - if !reflect.DeepEqual(gotLeft.Left(), "not even") { - t.Errorf("gotLeft %v, want %v", gotLeft.Left(), "not even") + + if !reflect.DeepEqual(*gotLeft.Left(), "not even") { + t.Errorf("gotLeft %v, want %v", *gotLeft.Left(), "not even") } } func TestEither_MapLeft(t *testing.T) { - left := Left("foo") + t.Parallel() - got := left.MapLeft(func(t string) string { + left := Left("foo") + got := MapLeft(left, func(t string) string { return t + "bar" }) - if !reflect.DeepEqual(got.Left(), "foobar") { - t.Errorf("MapLeft() = %v, want %v", got.Left(), "foobar") + if !reflect.DeepEqual(*got.Left(), "foobar") { + t.Errorf("MapLeft() = %v, want %v", *got.Left(), "foobar") } right := Right("foo") + called := false - var called = false - right.MapLeft(func(t types.Nil) types.Nil { + MapLeft(right, func(t string) string { called = true + return t }) - if !reflect.DeepEqual(right.Right(), "foo") { - t.Errorf("MapLeft() = %v, want %v", right.Right(), "foo") + if !reflect.DeepEqual(*right.Right(), "foo") { + t.Errorf("MapLeft() = %v, want %v", *right.Right(), "foo") } if !reflect.DeepEqual(called, false) { @@ -61,60 +68,41 @@ func TestEither_MapLeft(t *testing.T) { } func TestEither_MapRight(t *testing.T) { + t.Parallel() + left := Left("foo") + called := false - var called = false - left.MapRight(func(t types.Nil) types.Nil { + MapRight(left, func(t string) string { called = true + return t }) - if !reflect.DeepEqual(left.Left(), "foo") { - t.Errorf("MapRight() = %v, want %v", left.Left(), "foo") + if !reflect.DeepEqual(*left.Left(), "foo") { + t.Errorf("MapRight() = %v, want %v", *left.Left(), "foo") } + if !reflect.DeepEqual(called, false) { t.Errorf("MapRight() = %v, want %v", called, false) } right := Right("foo") - - got := right.MapRight(func(t string) string { - return t + "bar" + got := MapRight(right, func(t string) int { + return 123 }) - if !reflect.DeepEqual(got.Right(), "foobar") { - t.Errorf("MapRight() = %v, want %v", got.Right(), "foobar") + if !reflect.DeepEqual(*got.Right(), 123) { + t.Errorf("MapRight() = %v, want %v", *got.Right(), 123) } } -func TestEither_Left(t *testing.T) { - got := Left("foo").Left() - - if !reflect.DeepEqual(got, "foo") { - t.Errorf("Left() = %v, want %v", got, "foo") - } - - defer func() { recover() }() - Right("foo").Left() - t.Errorf("Left() on Right did not panic") -} - -func TestEither_Right(t *testing.T) { - got := Right("foo").Right() - - if !reflect.DeepEqual(got, "foo") { - t.Errorf("Right() = %v, want %v", got, "foo") - } - - defer func() { recover() }() - Left("foo").Right() - t.Errorf("Right() on Left did not panic") -} - func TestEither_IfLeft(t *testing.T) { + t.Parallel() + left := Left("foo") + called := false - var called = false left.IfLeft(func(t string) { called = true }) @@ -123,8 +111,9 @@ func TestEither_IfLeft(t *testing.T) { t.Errorf("IfLeft() called = %v, want %v", called, true) } - var called2 = false - newR[string, string]("foo").IfLeft(func(t string) { + called2 := false + + Right("foo").IfLeft(func(_ string) { called2 = true }) @@ -134,9 +123,11 @@ func TestEither_IfLeft(t *testing.T) { } func TestEither_IfRight(t *testing.T) { + t.Parallel() + right := Right("foo") + called := false - var called = false right.IfRight(func(t string) { called = true }) @@ -145,8 +136,9 @@ func TestEither_IfRight(t *testing.T) { t.Errorf("IfRight() called = %v, want %v", called, true) } - var called2 = false - newL[string, string]("foo").IfRight(func(t string) { + called2 := false + + Left("foo").IfRight(func(t string) { called2 = true }) @@ -156,13 +148,14 @@ func TestEither_IfRight(t *testing.T) { } func TestEither_LeftOr(t *testing.T) { - got := Left("foo").LeftOr("bar") + t.Parallel() + got := *(Left("foo").LeftOr("bar")) if !reflect.DeepEqual(got, "foo") { t.Errorf("LeftOr() = %v, want %v", got, "foo") } - got2 := newR[string, string]("foo").LeftOr("bar") + got2 := *(Right("foo").LeftOr("bar")) if !reflect.DeepEqual(got2, "bar") { t.Errorf("LeftOr() = %v, want %v", got2, "bar") @@ -170,13 +163,14 @@ func TestEither_LeftOr(t *testing.T) { } func TestEither_RightOr(t *testing.T) { - got := Right("foo").RightOr("bar") + t.Parallel() + got := *(Right("foo").RightOr("bar")) if !reflect.DeepEqual(got, "foo") { t.Errorf("RightOr() = %v, want %v", got, "foo") } - got2 := newL[string, string]("foo").RightOr("bar") + got2 := *(Left("foo").RightOr("bar")) if !reflect.DeepEqual(got2, "bar") { t.Errorf("RightOr() = %v, want %v", got2, "bar") @@ -184,19 +178,21 @@ func TestEither_RightOr(t *testing.T) { } func TestEither_LeftOrElse(t *testing.T) { + t.Parallel() + left := Left("foo") - got := left.LeftOrElse(func() string { + got := *(left.LeftOrElse(func() string { return "bar" - }) + })) if !reflect.DeepEqual(got, "foo") { t.Errorf("LeftOrElse() = %v, want %v", got, "foo") } - got2 := newR[string, string]("foo").LeftOrElse(func() string { + got2 := *(Right("foo").LeftOrElse(func() string { return "bar" - }) + })) if !reflect.DeepEqual(got2, "bar") { t.Errorf("LeftOrElse() = %v, want %v", got2, "bar") @@ -204,19 +200,21 @@ func TestEither_LeftOrElse(t *testing.T) { } func TestEither_RightOrElse(t *testing.T) { + t.Parallel() + right := Right("foo") - got := right.RightOrElse(func() string { + got := *(right.RightOrElse(func() string { return "bar" - }) + })) if !reflect.DeepEqual(got, "foo") { t.Errorf("RightOrElse() = %v, want %v", got, "foo") } - got2 := newL[string, string]("foo").RightOrElse(func() string { + got2 := *(Left("foo").RightOrElse(func() string { return "bar" - }) + })) if !reflect.DeepEqual(got2, "bar") { t.Errorf("RightOrElse() = %v, want %v", got2, "bar") @@ -224,9 +222,12 @@ func TestEither_RightOrElse(t *testing.T) { } func TestEither_IfNotLeft(t *testing.T) { + t.Parallel() + left := Left("foo") - var called = false + called := false + left.IfNotLeft(func() { called = true }) @@ -235,8 +236,9 @@ func TestEither_IfNotLeft(t *testing.T) { t.Errorf("IfNotLeft() called = %v, want %v", called, false) } - var called2 = false - newR[string, string]("foo").IfNotLeft(func() { + called2 := false + + Right("foo").IfNotLeft(func() { called2 = true }) @@ -246,9 +248,12 @@ func TestEither_IfNotLeft(t *testing.T) { } func TestEither_IfNotRight(t *testing.T) { + t.Parallel() + right := Right("foo") - var called = false + called := false + right.IfNotRight(func() { called = true }) @@ -257,8 +262,9 @@ func TestEither_IfNotRight(t *testing.T) { t.Errorf("IfNotRight() called = %v, want %v", called, false) } - var called2 = false - newL[string, string]("foo").IfNotRight(func() { + called2 := false + + Left("foo").IfNotRight(func() { called2 = true }) @@ -268,10 +274,13 @@ func TestEither_IfNotRight(t *testing.T) { } func TestEither_MaybeLeft(t *testing.T) { + t.Parallel() + got := Left("foo").MaybeLeft() if !got.IsSome() { t.Errorf("MaybeLeft() = %v, want %v", got.IsSome(), true) + if !reflect.DeepEqual(got.Unwrap(), "foo") { t.Errorf("MaybeLeft() = %v, want %v", got.Unwrap(), "foo") } @@ -285,10 +294,13 @@ func TestEither_MaybeLeft(t *testing.T) { } func TestEither_MaybeRight(t *testing.T) { + t.Parallel() + got := Right("foo").MaybeRight() if !got.IsSome() { t.Errorf("MaybeRight() = %v, want %v", got.IsSome(), true) + if !reflect.DeepEqual(got.Unwrap(), "foo") { t.Errorf("MaybeRight() = %v, want %v", got.Unwrap(), "foo") } @@ -302,12 +314,15 @@ func TestEither_MaybeRight(t *testing.T) { } func TestEither_Swap(t *testing.T) { + t.Parallel() + got := Right("foo").Swap() if !got.IsLeft() { t.Errorf("Swap() = %v, want %v", got.IsLeft(), true) - if !reflect.DeepEqual(got.Left(), "foo") { - t.Errorf("Swap() = %v, want %v", got.Left(), "foo") + + if !reflect.DeepEqual(*got.Left(), "foo") { + t.Errorf("Swap() = %v, want %v", *got.Left(), "foo") } } @@ -315,8 +330,9 @@ func TestEither_Swap(t *testing.T) { if !got2.IsRight() { t.Errorf("Swap() = %v, want %v", got2.IsRight(), true) - if !reflect.DeepEqual(got2.Right(), "foo") { - t.Errorf("Swap() = %v, want %v", got2.Right(), "foo") + + if !reflect.DeepEqual(*got2.Right(), "foo") { + t.Errorf("Swap() = %v, want %v", *got2.Right(), "foo") } } } diff --git a/either/left.go b/either/left.go index e2791fd..418747a 100644 --- a/either/left.go +++ b/either/left.go @@ -1,29 +1,38 @@ package either -import "go/types" - -type left[L any, R any] struct { - *either[L, R] +type left[L, R any] struct { + Either[L, R] val L } -func newL[L any, R any](val L) *left[L, R] { - l := &left[L, R]{ - val: val, - } - - l.either = &either[L, R]{ +func newL[L, R any](val L) Either[L, R] { + l := new(left[L, R]) + l.val = val + l.Either = &either[L, R]{ Either: l, - left: l, } + return l } /* Left returns a new Either value with Left as the passed argument. +By default, the Right Type is the same as the Left Type. */ -func Left[L any](value L) Either[L, types.Nil] { - return newL[L, types.Nil](value) +func Left[L any](value L) Either[L, L] { + return newL[L, L](value) +} + +func (l left[L, R]) Left() *L { + return &l.val +} + +func (l left[L, R]) Right() *R { + panic(any("called Right on Left")) +} + +func (l left[L, R]) IsLeft() bool { + return true } func (l left[L, R]) IsRight() bool { diff --git a/either/left_test.go b/either/left_test.go index 9bf73f4..e44fef3 100644 --- a/either/left_test.go +++ b/either/left_test.go @@ -1,20 +1,35 @@ -package either +package either_test import ( "reflect" "testing" + + "github.com/medmouine/gomad/either" ) func TestLeft(t *testing.T) { - got := Left("foo") + t.Parallel() + got := either.Left("foo") if !reflect.DeepEqual(got.IsLeft(), true) { t.Errorf("Left() = %v, want %v", got.IsLeft(), true) } - if !reflect.DeepEqual(got.Left(), "foo") { - t.Errorf("Left() = %v, want %v", got.Left(), "foo") + + if !reflect.DeepEqual(*got.Left(), "foo") { + t.Errorf("Left() = %v, want %v", *got.Left(), "foo") } + if !reflect.DeepEqual(got.IsRight(), false) { t.Errorf("Left() = %v, want %v", got.IsRight(), false) } + + defer func() { + err := recover() + if err == nil { + t.Errorf("Right() on Left did not panic") + } + + t.Logf("PASS: Right() on left panic msg: %v", err) + }() + either.Left("foo").Right() } diff --git a/either/right.go b/either/right.go index 31724bc..bbcba33 100644 --- a/either/right.go +++ b/either/right.go @@ -1,29 +1,39 @@ package either -import "go/types" - -type right[L any, R any] struct { - *either[L, R] +type right[L, R any] struct { + Either[L, R] val R } -func newR[L any, R any](val R) *right[L, R] { - r := &right[L, R]{ - val: val, - } - - r.either = &either[L, R]{ +func newR[L, R any](val R) *right[L, R] { + r := new(right[L, R]) + r.val = val + r.Either = &either[L, R]{ Either: r, - right: r, } + return r } /* Right returns a new Either value with Right as the passed argument. +By default, the Left Type is the same as the Right Type. + */ -func Right[R any](value R) Either[types.Nil, R] { - return newR[types.Nil, R](value) +func Right[R any](value R) Either[R, R] { + return newR[R, R](value) +} + +func (r right[L, R]) Right() *R { + return &r.val +} + +func (r right[L, R]) Left() *L { + panic(any("called Left on Right")) +} + +func (r right[L, R]) IsLeft() bool { + return false } func (r right[L, R]) IsRight() bool { diff --git a/either/right_test.go b/either/right_test.go index 2244736..a01473c 100644 --- a/either/right_test.go +++ b/either/right_test.go @@ -1,20 +1,35 @@ -package either +package either_test import ( "reflect" "testing" + + "github.com/medmouine/gomad/either" ) func TestRight(t *testing.T) { - got := Right("foo") + t.Parallel() + got := either.Right("foo") if !reflect.DeepEqual(got.IsLeft(), false) { t.Errorf("Right() = %v, want %v", got.IsLeft(), false) } - if !reflect.DeepEqual(got.Right(), "foo") { - t.Errorf("Right() = %v, want %v", got.Right(), "foo") + + if !reflect.DeepEqual(*got.Right(), "foo") { + t.Errorf("Right() = %v, want %v", *got.Right(), "foo") } + if !reflect.DeepEqual(got.IsRight(), true) { t.Errorf("Right() = %v, want %v", got.IsRight(), true) } + + defer func() { + err := recover() + if err == nil { + t.Errorf("Left() on Right did not panic") + } + + t.Logf("PASS: Left() on Right panic msg: %v", err) + }() + either.Right("foo").Left() } diff --git a/eq/eq_test.go b/eq/eq_test.go index dd674d0..93e19a6 100644 --- a/eq/eq_test.go +++ b/eq/eq_test.go @@ -1,8 +1,14 @@ -package eq +package eq_test -import "testing" +import ( + "testing" + + . "github.com/medmouine/gomad/eq" +) func TestFromEquals(t *testing.T) { + t.Parallel() + intEq := func(x int, y int) bool { return x == y } got := FromEquals(intEq) @@ -17,12 +23,15 @@ type Person struct { } func Test_eq_Equals(t *testing.T) { + t.Parallel() + intEq := func(x int, y int) bool { return x == y } got := FromEquals(intEq) if got.Equals(1, 2) { t.Errorf("eq.Equals() = %v, want %v", got.Equals(1, 2), false) } + if !got.Equals(1, 1) { t.Errorf("eq.Equals() = %v, want %v", got.Equals(1, 1), true) } @@ -33,6 +42,7 @@ func Test_eq_Equals(t *testing.T) { if got2.Equals("a", "b") { t.Errorf("eq.Equals() = %v, want %v", got2.Equals("a", "b"), false) } + if !got2.Equals("a", "a") { t.Errorf("eq.Equals() = %v, want %v", got2.Equals("a", "a"), true) } diff --git a/functor/functor.go b/functor/functor.go index ed8ca3a..6711982 100644 --- a/functor/functor.go +++ b/functor/functor.go @@ -5,25 +5,25 @@ type IFunctor[T any] interface { } type Functor[T any] struct { - val T + Val T } func Lift[T any, U any](f func(T) U) func(Functor[T]) Functor[U] { return func(fa Functor[T]) Functor[U] { return Functor[U]{ - val: f(fa.val), + Val: f(fa.Val), } } } func Map[T any, U any](fa Functor[T], f func(T) U) Functor[U] { return Functor[U]{ - val: f(fa.val), + Val: f(fa.Val), } } func (fa Functor[T]) Map(f func(T) T) Functor[T] { return Functor[T]{ - val: f(fa.val), + Val: f(fa.Val), } } diff --git a/functor/functor_test.go b/functor/functor_test.go index db3b59b..7f54a83 100644 --- a/functor/functor_test.go +++ b/functor/functor_test.go @@ -1,64 +1,68 @@ -package functor +package functor_test import ( - "github.com/medmouine/gomad/identity" "strconv" "testing" + + . "github.com/medmouine/gomad/functor" + "github.com/medmouine/gomad/identity" ) func TestLift(t *testing.T) { - intToString := func(i int) string { - return strconv.Itoa(i) - } + t.Parallel() + + intToString := strconv.Itoa got := Lift(intToString) intFunctor := Functor[int]{ - val: 1, + Val: 1, } - if got(intFunctor).val != "1" { + if got(intFunctor).Val != "1" { t.Errorf("Lift(intToString) failed") } got2 := Lift(identity.Identity[int]) - if got2(intFunctor).val != 1 { + if got2(intFunctor).Val != 1 { t.Errorf("Lift(identity.Identity) failed") } } func TestMap(t *testing.T) { - intToString := func(i int) string { - return strconv.Itoa(i) - } + t.Parallel() + + intToString := strconv.Itoa intFunctor := Functor[int]{ - val: 1, + Val: 1, } got := Map(intFunctor, intToString) - if got.val != "1" { + if got.Val != "1" { t.Errorf("Map(intToString) failed") } got2 := Map(intFunctor, identity.Identity[int]) - if got2.val != 1 { + if got2.Val != 1 { t.Errorf("Map(identity.Identity) failed") } } func TestFunctor_Map(t *testing.T) { + t.Parallel() + intFunctor := Functor[int]{ - val: 1, + Val: 1, } got := intFunctor.Map(func(i int) int { return 5 }) - if got.val != 5 { + if got.Val != 5 { t.Errorf("Map(intToString) failed") } } diff --git a/identity/identity.go b/identity/identity.go index 002b077..80785df 100644 --- a/identity/identity.go +++ b/identity/identity.go @@ -1,5 +1,5 @@ package identity -func Identity[T any](i T) T { - return i +func Identity[T any](i T) *T { + return &i } diff --git a/maybe/maybe.go b/maybe/maybe.go index d8b1e49..7b6ec45 100644 --- a/maybe/maybe.go +++ b/maybe/maybe.go @@ -8,19 +8,20 @@ type nillable interface { /* Maybe is a monadic pattern allowing for data manipulation while abstracting whether the value actually exists or is nil. -For example, if we fetch data from an external API that could be nil, we can still perform manipulation on it while disregarding its actual state. -The Maybe struct will take care of managing the value itself. This is similar to the Maybe interface in Elm or Haskell or Optional in Java. +For example, if we fetch data from an external API that could be nil, we can still perform manipulation on it while +disregarding its actual state. The Maybe struct will take care of managing the value itself. This is similar to +the Maybe interface in Elm or Haskell or Optional in Java. This is helpful for CRUD operations by simplifying the code and allowing for seamless manipulation of nullable data. */ type Maybe[T any] interface { - Unwrap() T + Unwrap() *T Map(func(T) T) Maybe[T] Apply(func(T)) Maybe[T] Bind(func(T) Maybe[T]) Maybe[T] IsSome() bool IsNil() bool - OrElse(func() T) T - Or(T) T + OrElse(func() T) *T + Or(T) *T OrNil() *T } @@ -29,6 +30,14 @@ type maybe[T any] struct { val *T } +func Map[T2, T any](m Maybe[T], f func(T) T2) Maybe[T2] { + if m.IsSome() { + return Just(f(*m.Unwrap())) + } + + return None[T2]() +} + /* Of returns a new Maybe based on a value that may or may not be nil. */ @@ -36,6 +45,7 @@ func Of[T nillable](val *T) Maybe[T] { if val == nil { return maybe[T]{val: nil} } + return maybe[T]{val: val} } @@ -53,10 +63,11 @@ func None[T nillable]() Maybe[T] { return maybe[T]{val: nil} } -func (m maybe[T]) Unwrap() T { +func (m maybe[T]) Unwrap() *T { if m.IsSome() { - return *m.val + return m.val } + panic(any("unwrap of empty Maybe")) } @@ -64,13 +75,15 @@ func (m maybe[T]) Map(f func(T) T) Maybe[T] { if m.IsSome() { return Just(f(*m.val)) } + return m } func (m maybe[T]) Apply(f func(x T)) Maybe[T] { if m.IsSome() { - f(m.Unwrap()) + f(*m.Unwrap()) } + return m } @@ -82,10 +95,12 @@ func (m maybe[T]) IsNil() bool { return m.val == nil } -func (m maybe[T]) OrElse(f func() T) T { +func (m maybe[T]) OrElse(f func() T) *T { if m.IsNil() { - return f() + v := f() + return &v } + return m.Unwrap() } @@ -93,19 +108,22 @@ func (m maybe[T]) OrNil() *T { if m.IsNil() { return nil } + return m.val } -func (m maybe[T]) Or(val T) T { +func (m maybe[T]) Or(val T) *T { if m.IsNil() { - return val + return &val } + return m.Unwrap() } func (m maybe[T]) Bind(f func(T) Maybe[T]) Maybe[T] { if m.IsSome() { - return f(m.Unwrap()) + return f(*m.Unwrap()) } + return None[T]() } diff --git a/maybe/maybe_test.go b/maybe/maybe_test.go index 2fd349b..11f5b9e 100644 --- a/maybe/maybe_test.go +++ b/maybe/maybe_test.go @@ -1,17 +1,22 @@ -package maybe +package maybe_test import ( "reflect" "testing" + + . "github.com/medmouine/gomad/maybe" ) func TestMaybe_Just(t *testing.T) { + t.Parallel() + integer := 123 gotInt := Just(integer) if !reflect.DeepEqual(gotInt.Unwrap(), integer) { t.Errorf("Just() = %v, want %v", gotInt.Unwrap(), integer) } + if !reflect.DeepEqual(gotInt.IsNil(), false) { t.Errorf("Just() = %v, want %v", gotInt.IsNil(), false) } @@ -32,20 +37,24 @@ func TestMaybe_Just(t *testing.T) { } func TestMaybe_None(t *testing.T) { - got := None[int]() + t.Parallel() + got := None[int]() if !got.IsNil() { t.Errorf("None() = %v, want %v", got.IsNil(), false) } } func TestMaybe_Of(t *testing.T) { + t.Parallel() + integer := 123 gotInteger := Of(&integer) if !reflect.DeepEqual(gotInteger.Unwrap(), integer) { t.Errorf("Of() = %v, want %v", gotInteger.Unwrap(), integer) } + if gotInteger.IsNil() { t.Errorf("Of() = %v, want %v", gotInteger.IsNil(), false) } @@ -58,10 +67,13 @@ func TestMaybe_Of(t *testing.T) { } func TestMaybe_Apply(t *testing.T) { + t.Parallel() + integer := 1 m := Just(integer) - var called = false + called := false + m.Apply(func(v int) { called = true }) @@ -69,11 +81,13 @@ func TestMaybe_Apply(t *testing.T) { if !called { t.Errorf("Apply() called = %v, want %v", called, true) } + if !reflect.DeepEqual(m.Unwrap(), integer) { t.Errorf("Apply() = %v, want %v", m.Unwrap(), integer) } - var called2 = false + called2 := false + None[int]().Apply(func(v int) { called2 = true }) @@ -84,13 +98,15 @@ func TestMaybe_Apply(t *testing.T) { } func TestMaybe_Map(t *testing.T) { + t.Parallel() + got := Just(1).Map(func(v int) int { return v + 3 }) - if !reflect.DeepEqual(got.Unwrap(), 4) { t.Errorf("Map() = %v, want %v", got.Unwrap(), 4) } + if !got.IsSome() { t.Errorf("Map() = %v, want %v", got.IsSome(), true) } @@ -98,31 +114,33 @@ func TestMaybe_Map(t *testing.T) { got2 := None[int]().Map(func(v int) int { return v + 3 }) - if !got2.IsNil() { t.Errorf("Map() = %v, want %v", got2.IsNil(), true) } } func TestMaybe_Unwrap(t *testing.T) { + t.Parallel() - got := Just(1).Unwrap() - - if !reflect.DeepEqual(got, 1) { + if got := Just(1).Unwrap(); !reflect.DeepEqual(got, 1) { t.Errorf("Unwrap() = %v, want %v", got, 1) } - defer func() { recover() }() + defer func() { + err := recover() + if err == nil { + t.Errorf("Unwrap() on Nil did not panic") + } + }() None[int]().Unwrap() - t.Errorf("Unwrap() on Nil did not panic") } func TestMaybe_OrElse(t *testing.T) { + t.Parallel() got := None[int]().OrElse(func() int { return 3 }) - if !reflect.DeepEqual(got, 3) { t.Errorf("OrElse() = %v, want %v", got, 3) } @@ -130,74 +148,67 @@ func TestMaybe_OrElse(t *testing.T) { got2 := Just(1).OrElse(func() int { return 3 }) - if !reflect.DeepEqual(got2, 1) { t.Errorf("OrElse() = %v, want %v", got2, 1) } } func TestMaybe_Or(t *testing.T) { + t.Parallel() - got := None[int]().Or(3) - - if !reflect.DeepEqual(got, 3) { + if got := None[int]().Or(3); !reflect.DeepEqual(got, 3) { t.Errorf("Or() = %v, want %v", got, 3) } - got2 := Just(1).Or(3) - - if !reflect.DeepEqual(got2, 1) { + if got2 := Just(1).Or(3); !reflect.DeepEqual(got2, 1) { t.Errorf("Or() = %v, want %v", got2, 1) } } func TestMaybe_OrNil(t *testing.T) { - got := None[int]().OrNil() + t.Parallel() + got := None[int]().OrNil() if got != nil { t.Errorf("OrNil() = %v, want %v", got, nil) } got2 := Just(4).OrNil() - if !reflect.DeepEqual(*got2, 4) { t.Errorf("OrNil() = %v, want %v", *got2, 4) } } func TestMaybe_IsNone(t *testing.T) { - got := None[int]().IsNil() + t.Parallel() - if !got { + if got := None[int]().IsNil(); !got { t.Errorf("IsNil() = %v, want %v", got, true) } - got2 := Just(4).IsNil() - - if got2 { + if got2 := Just(4).IsNil(); got2 { t.Errorf("IsNil() = %v, want %v", got2, false) } } func TestMaybe_IsSome(t *testing.T) { - got := Just(1).IsSome() + t.Parallel() - if !got { + if got := Just(1).IsSome(); !got { t.Errorf("IsSome() = %v, want %v", got, true) } - got2 := None[int]().IsSome() - - if got2 { + if got2 := None[int]().IsSome(); got2 { t.Errorf("IsSome() = %v, want %v", got2, false) } } func TestMaybe_Bind(t *testing.T) { + t.Parallel() + got := Just(2).Bind(func(t int) Maybe[int] { return Just(t * t) }) - if !reflect.DeepEqual(got, Just(4)) { t.Errorf("Bind() = %v, want %v", got, Just(4)) } @@ -205,9 +216,7 @@ func TestMaybe_Bind(t *testing.T) { got2 := None[int]().Bind(func(t int) Maybe[int] { return Just(t * t) }) - if !reflect.DeepEqual(got2, None[int]()) { t.Errorf("Bind() = %v, want %v", got2, None[int]()) } - } diff --git a/monoid/monoid_test.go b/monoid/monoid_test.go index 9c4cdcc..2794c56 100644 --- a/monoid/monoid_test.go +++ b/monoid/monoid_test.go @@ -1,8 +1,9 @@ package monoid import ( - "github.com/medmouine/gomad/semigroup" "testing" + + "github.com/medmouine/gomad/semigroup" ) func TestFromConcat(t *testing.T) { diff --git a/result/result_test.go b/result/result_test.go index 193dd6d..c62f8b1 100644 --- a/result/result_test.go +++ b/result/result_test.go @@ -2,9 +2,10 @@ package result import ( "errors" - "github.com/medmouine/gomad/maybe" "reflect" "testing" + + "github.com/medmouine/gomad/maybe" ) func TestOk(t *testing.T) { @@ -100,7 +101,7 @@ func TestResult_Err(t *testing.T) { } func TestResult_IfErr(t *testing.T) { - var called = false + called := false Err[int](errors.New("error")).IfErr(func(err error) { called = true }) @@ -109,7 +110,7 @@ func TestResult_IfErr(t *testing.T) { t.Errorf("IfErr() = %v, want %v", called, true) } - var called2 = false + called2 := false Ok(1).IfErr(func(err error) { called2 = true }) @@ -120,7 +121,7 @@ func TestResult_IfErr(t *testing.T) { } func TestResult_IfOk(t *testing.T) { - var called = false + called := false Err[int](errors.New("error")).IfOk(func(i int) { called = true }) @@ -129,7 +130,7 @@ func TestResult_IfOk(t *testing.T) { t.Errorf("IfOk() = %v, want %v", called, false) } - var called2 = false + called2 := false Ok(1).IfOk(func(i int) { called2 = true }) diff --git a/semigroup/semigroup.go b/semigroup/semigroup.go index d207404..9cf20bf 100644 --- a/semigroup/semigroup.go +++ b/semigroup/semigroup.go @@ -20,7 +20,7 @@ func FoldF[T any](sg Semigroup[T]) func([]T) T { func FoldMap[T any](sg Semigroup[T], f func(T) T, init T) func([]T) T { return func(xs []T) T { - var acc = f(init) + acc := f(init) for _, x := range xs { acc = sg.Concat(acc, f(x)) } @@ -30,7 +30,7 @@ func FoldMap[T any](sg Semigroup[T], f func(T) T, init T) func([]T) T { func FoldMapF[T any](sg Semigroup[T], f func(T) T) func([]T) T { return func(xs []T) T { - var acc = f(xs[0]) + acc := f(xs[0]) for _, x := range xs[1:] { acc = sg.Concat(acc, f(x)) }