golang实现Monad和函数式编程特性的插件库fpGo的使用

Golang 实现 Monad 和函数式编程特性的插件库 fpGo 的使用

fpGo 是一个为 Golang 提供 Monad 和函数式编程特性的库,包含 Optional/Maybe、Monad、Rx-like 编程、Actor 模型等多种功能。

安装

对于 Go 1.18+ 版本(支持泛型):

go get github.com/TeaEntityLab/fpGo/v2

对于 Go 1.17 及以下版本(不支持泛型):

go get github.com/TeaEntityLab/fpGo

主要功能

Optional/Maybe

var m MaybeDef[interface{}]
var orVal int
var boolVal bool

// IsPresent(), IsNil()
m = Maybe.Just(1)
boolVal = m.IsPresent() // true
boolVal = m.IsNil()     // false
m = Maybe.Just(nil)
boolVal = m.IsPresent() // false
boolVal = m.IsNil()     // true

// Or()
m = Maybe.Just(1)
fmt.Println((m.Or(3))) // 1
m = Maybe.Just(nil)
fmt.Println((m.Or(3))) // 3

// Let()
var letVal int
letVal = 1
m = Maybe.Just(1)
m.Let(func() {
  letVal = 2
})
fmt.Println(letVal) // letVal 会变成 2

letVal = 1
m = Maybe.Just(nil)
m.Let(func() {
  letVal = 3
})
fmt.Println(letVal) // letVal 仍然是 1

MonadIO (RxObserver 风格)

var m *MonadIODef[interface{}]
var actualInt int

m = MonadIO.Just(1)
actualInt = 0
m.Subscribe(Subscription[interface{}]{
  OnNext: func(in interface{}) {
    actualInt, _ = Maybe.Just(in).ToInt()
  },
})
fmt.Println(actualInt) // actualInt 会是 1

m = MonadIO.Just(1).FlatMap(func(in interface{}) *MonadIODef[interface{}] {
  v, _ := Maybe.Just(in).ToInt()
  return MonadIO.Just(v + 1)
})
actualInt = 0
m.Subscribe(Subscription[interface{}]{
  OnNext: func(in interface{}) {
    actualInt, _ = Maybe.Just(in).ToInt()
  },
})
fmt.Println(actualInt) // actualInt 会是 2

Stream (集合操作)

泛型版本示例:

var s *StreamDef[int]
var tempString = ""

s = StreamFromArray([]int{}).Append(1, 1).Extend(StreamFromArray([]int{2, 3, 4}))
tempString = ""
for _, v := range s.ToArray() {
  tempString += Maybe.Just(v).ToMaybe().ToString()
}
fmt.Println(tempString) // tempString 会是 "11234"

s = s.Distinct()
tempString = ""
for _, v := range s.ToArray() {
  tempString += Maybe.Just(v).ToMaybe().ToString()
}
fmt.Println(tempString) // tempString 会是 "1234"

非泛型版本示例:

var s *StreamForInterfaceDef
var tempString string

s = StreamForInterface.FromArrayInt([]int{}).Append(1, 1).Extend(StreamForInterface.FromArrayInt([]int{2, 3, 4})).Extend(StreamForInterface.FromArray([]interface{}{nil})).Extend(nil)
tempString = ""
for _, v := range s.ToArray() {
  tempString += Maybe.Just(v).ToMaybe().ToString()
}
fmt.Println(tempString) // tempString 会是 "11234<nil>"

s = s.Distinct()
tempString = ""
for _, v := range s.ToArray() {
  tempString += Maybe.Just(v).ToMaybe().ToString()
}
fmt.Println(tempString) // tempString 会是 "1234<nil>"

s = s.FilterNotNil()
tempString = ""
for _, v := range s.ToArray() {
  tempString += Maybe.Just(v).ToMaybe().ToString()
}
fmt.Println(tempString) // tempString 会是 "1234"

Actor 模型 (受 Akka/Erlang 启发)

actual := 0
// 结果通道
resultChannel := make(chan interface{}, 1)
// 消息命令
cmdSpawn := "spawn"
cmdShutdown := "shutdown"
// 测试 Actor
actorRoot := Actor.New(func(self *ActorDef[interface{}], input interface{}) {
  // SPAWN: 根 Actor
  if input == cmdSpawn {
    self.Spawn(func(self *ActorDef[interface{}], input interface{}) {
      // SHUTDOWN: 子 Actor
      if input == cmdShutdown {
        self.Close()
        return
      }

      // INT 处理: 子 Actor
      val, _ := Maybe.Just(input).ToInt()
      resultChannel <- val * 10
    })
    return
  }
  // SHUTDOWN: 根 Actor
  if input == cmdShutdown {
    for _, child := range self.children {
      child.Send(cmdShutdown)
    }
    self.Close()

    close(resultChannel)
    return
  }

  // INT 处理: 根 Actor
  intVal, _ := Maybe.Just(input).ToInt()
  if intVal > 0 {
    for _, child := range self.children {
      child.Send(intVal)
    }
  }
})

// 顺序发送消息(异步)
go func() {
  actorRoot.Send(cmdSpawn)
  actorRoot.Send(10)
  actorRoot.Send(cmdSpawn)
  actorRoot.Send(20)
  actorRoot.Send(cmdSpawn)
  actorRoot.Send(30)
}()

i := 0
for val := range resultChannel {
  intVal, _ := Maybe.Just(val).ToInt()
  actual += intVal

  i++
  if i == 5 {
    go actorRoot.Send(cmdShutdown)
  }
}

// 结果会是 1400 (=10*10+20*10+20*10+30*10+30*10+30*10)
fmt.Println(actual)

函数组合

var fn01 = func(args ...int) []int {
  val := args[0]
  return SliceOf(val + 1)
}
var fn02 = func(args ...int) []int {
  val := args[0]
  return SliceOf(val + 2)
}
var fn03 = func(args ...int) []int {
  val := args[0]
  return SliceOf(val + 3)
}

// 结果会是 6
result := Compose(fn01, fn02, fn03)((0))[0]

fpGo 提供了丰富的函数式编程特性,可以帮助 Golang 开发者以更函数式的方式编写代码。


更多关于golang实现Monad和函数式编程特性的插件库fpGo的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现Monad和函数式编程特性的插件库fpGo的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用fpGo实现Golang中的Monad和函数式编程

fpGo是一个为Golang提供函数式编程特性的库,它实现了Monad、Functor等函数式编程概念。下面我将详细介绍如何使用fpGo来实现Monad和函数式编程特性。

安装fpGo

首先安装fpGo库:

go get github.com/TeaEntityLab/fpGo

基础概念

1. Option Monad

Option Monad用于处理可能存在或不存在的值:

package main

import (
	"fmt"
	"github.com/TeaEntityLab/fpGo"
)

func main() {
	// 创建Some和None
	some := fpGo.Some(42)
	none := fpGo.None[int]()

	// 使用Map转换值
	mappedSome := fpGo.MapOption(some, func(x int) int { return x * 2 })
	mappedNone := fpGo.MapOption(none, func(x int) int { return x * 2 })

	fmt.Println(mappedSome) // Some(84)
	fmt.Println(mappedNone) // None

	// 使用FlatMap链式操作
	result := fpGo.FlatMapOption(some, func(x int) fpGo.Option[int] {
		if x > 40 {
			return fpGo.Some(x + 10)
		}
		return fpGo.None[int]()
	})
	fmt.Println(result) // Some(52)
}

2. Either Monad

Either表示两种可能的值,常用于错误处理:

package main

import (
	"fmt"
	"github.com/TeaEntityLab/fpGo"
)

func divide(a, b int) fpGo.Either[string, int] {
	if b == 0 {
		return fpGo.Left[string, int]("division by zero")
	}
	return fpGo.Right[string, int](a / b)
}

func main() {
	// 成功情况
	result1 := divide(10, 2)
	fmt.Println(result1) // Right(5)

	// 错误情况
	result2 := divide(10, 0)
	fmt.Println(result2) // Left("division by zero")

	// 处理Either
	fpGo.MatchEither(result1,
		func(err string) { fmt.Println("Error:", err) },
		func(val int) { fmt.Println("Result:", val) },
	)
}

3. Future Monad

Future用于处理异步操作:

package main

import (
	"fmt"
	"time"
	"github.com/TeaEntityLab/fpGo"
)

func asyncTask() fpGo.Future[int] {
	return fpGo.NewFuture(func() (int, error) {
		time.Sleep(1 * time.Second)
		return 42, nil
	})
}

func main() {
	future := asyncTask()

	// 链式操作
	resultFuture := fpGo.MapFuture(future, func(x int) int {
		return x * 2
	})

	// 获取结果
	result, err := resultFuture.Await()
	if err != nil {
		fmt.Println("Error:", err)
		return
	}
	fmt.Println("Result:", result) // 84
}

高级用法

1. 函数组合

package main

import (
	"fmt"
	"github.com/TeaEntityLab/fpGo"
)

func addOne(x int) int { return x + 1 }
func double(x int) int { return x * 2 }

func main() {
	// 组合函数
	composed := fpGo.Compose(addOne, double)

	fmt.Println(composed(5)) // (5 + 1) * 2 = 12
}

2. 柯里化

package main

import (
	"fmt"
	"github.com/TeaEntityLab/fpGo"
)

func add(a, b int) int { return a + b }

func main() {
	// 柯里化函数
	curriedAdd := fpGo.Curry2(add)
	addFive := curriedAdd(5)

	fmt.Println(addFive(3)) // 8
}

3. 管道操作

package main

import (
	"fmt"
	"github.com/TeaEntityLab/fpGo"
)

func main() {
	result := fpGo.Pipe2(
		5,
		func(x int) int { return x + 1 },
		func(x int) int { return x * 2 },
	)

	fmt.Println(result) // (5 + 1) * 2 = 12
}

实际应用示例

下面是一个结合多种Monad的实际示例:

package main

import (
	"fmt"
	"github.com/TeaEntityLab/fpGo"
)

type User struct {
	ID   int
	Name string
}

func getUser(id int) fpGo.Future[fpGo.Option[User]] {
	return fpGo.NewFuture(func() (fpGo.Option[User], error) {
		// 模拟数据库查询
		if id == 1 {
			return fpGo.Some(User{ID: 1, Name: "Alice"}), nil
		}
		return fpGo.None[User](), nil
	})
}

func main() {
	// 链式操作:Future -> Option -> Either
	resultFuture := fpGo.FlatMapFuture(getUser(1), func(userOpt fpGo.Option[User]) fpGo.Future[fpGo.Either[string, string]] {
		return fpGo.NewFuture(func() (fpGo.Either[string, string], error) {
			return fpGo.MatchOption(userOpt,
				func() fpGo.Either[string, string] {
					return fpGo.Left[string, string]("User not found")
				},
				func(user User) fpGo.Either[string, string] {
					return fpGo.Right[string, string]("Hello, " + user.Name)
				},
			), nil
		}
	})

	// 获取最终结果
	result, _ := resultFuture.Await()
	
	fpGo.MatchEither(result,
		func(err string) { fmt.Println("Error:", err) },
		func(msg string) { fmt.Println(msg) }, // Hello, Alice
	)
}

总结

fpGo为Golang带来了函数式编程的强大特性,包括:

  1. Monad实现:Option、Either、Future等
  2. 函数组合:Compose、Pipe等
  3. 柯里化:支持多参数函数的柯里化
  4. 不可变数据:所有操作都不会修改原始数据

使用fpGo可以使Golang代码更加声明式、可组合,并更好地处理错误和异步操作。虽然Golang不是函数式语言,但通过这些库可以实现许多函数式编程的优点。

注意:fpGo仍在发展中,API可能会有变化,建议查看最新文档获取更新信息。

回到顶部