golang实现TypeScript风格模式匹配功能的插件库go-pattern-match的使用

Golang实现TypeScript风格模式匹配功能的插件库go-pattern-match的使用

什么是模式匹配?

模式匹配起源于ML、Haskell和Erlang等函数式编程语言。它提供了一种强大的技术,用于基于输入与模式的匹配来控制流程。

与使用条件语句和switch语句的命令式控制流相比,模式匹配能够实现更简洁和声明式的代码,特别是在处理复杂的条件逻辑时。它避免了冗长的样板代码,更好地传达了意图。

关键组件

  • Patterner: 这是一个需要实现Match函数的接口。任何实现此接口的类型都可以用作匹配器中的模式。
  • Handler: 这是一个返回泛型类型T的函数类型。当找到匹配时调用此函数。
  • Matcher: 这是一个结构体,保存要匹配的值、指示是否找到匹配的标志以及找到匹配时要返回的响应。

基本使用方法

package main

import (
  "fmt"
  "github.com/phakornkiong/go-pattern-match/pattern"
)

func match(input []int) string {
  return pattern.NewMatcher[string](input).
    WithValues(
      []any{1, 2, 3, 4},
      func() string { return "Nope" },
    ).
    WithValues(
      []any{
        pattern.Any(),
        pattern.Not(36),
        pattern.Union[int](99, 98),
        255,
      },
      func() string { return "Its a match" },
    ).
    Otherwise(func() string { return "Otherwise" })
}

func main() {
  fmt.Println(match([]int{1, 2, 3, 4}))      // "Nope"
  fmt.Println(match([]int{25, 35, 99, 255})) // "Its a match"
  fmt.Println(match([]int{1, 5, 6, 7}))      // "Otherwise"
}

主要API方法

NewMatcher[T any, V any](input V) *Matcher[T, V]

创建一个新的Matcher实例。泛型TV代表任何类型。

T是处理函数在找到匹配时将返回的类型。 V是将与模式匹配的输入值的类型。

.WithPattern(pattern Patterner, fn Handler[T]) *Matcher[T, V]

检查提供的模式是否匹配整个输入。如果找到匹配,则调用提供的Handler函数并返回响应T

.WithPatterns(patterns []Patterner, fn Handler[T]) *Matcher[T, V]

检查每个提供的模式是否匹配每个输入。如果找到匹配,则调用提供的Handler函数并返回响应T

.WithValue(value V, fn Handler[T]) *Matcher[T, V]

检查提供的值与整个输入之间是否存在深度相等。如果找到匹配,则调用提供的Handler函数并返回响应T

.WithValues(values any, fn Handler[T]) *Matcher[T, V]

如果模式作为模式提供,它将通过在每个值上调用Match方法来检查任何提供的值是否匹配输入,否则将对每个值进行深度相等检查。如果找到匹配,则调用提供的Handler函数并返回响应T

.Otherwise(fn Handler[T]) *Matcher[T, V]

当没有找到输入的匹配时调用。它调用提供的Handler函数并返回响应T

常用模式示例

Any模式

func match(input int) string {
  return pattern.NewMatcher[string](input).
    WithValue(
      2,
      func() string { return "Nope" },
    ).
    WithPattern(
      pattern.Any(), // 总是匹配
      func() string { return "Its a match" },
    ).
    Otherwise(func() string { return "Nope" })
}

match(5) // "Its a match"
match(6) // "Its a match"
match(7) // "Its a match"

Not模式

func match(input int) string {
  return pattern.NewMatcher[string](input).
    WithValue(
      2,
      func() string { return "2" },
    ).
    WithPattern(
      pattern.Not(3), // 如果不是3则匹配
      func() string { return "Its a match" },
    ).
    Otherwise(func() string { return "Otherwise" })
}

match(3) // "Otherwise"
match(6) // "Its a match"
match(7) // "Its a match"

When模式

func match(input int) string {
  return pattern.NewMatcher[string](input).
    WithValue(
      2,
      func() string { return "2" },
    ).
    WithPattern(
      // 如果输入大于100则匹配
      pattern.When[int](func(i int) bool { return i > 100 }),
      func() string { return "Its a match" },
    ).
    Otherwise(func() string { return "Otherwise" })
}

match(2)   // "2"
match(99)  // "Otherwise"
match(100) // "Its a match"
match(105) // "Its a match"

Union模式

func FoodSorterWithPattern(input string) (output string) {
  output = pattern.NewMatcher[string](input).
    WithPattern(
      pattern.Union("apple", "strawberry", "orange"),
      func() string { return "fruit" },
    ).
    WithPattern(
      pattern.Union("carrot", "pok-choy", "cabbage"),
      func() string { return "vegetable" },
    ).
    Otherwise(func() string { return "unknown" })

  return output
}

FoodSorterWithPattern("apple")  // "fruit"
FoodSorterWithPattern("orange") // "fruit"
FoodSorterWithPattern("carrot") // "vegetable"
FoodSorterWithPattern("candy")  // "unknown"

String模式

func match(input string) string {
  pattern1 := pattern.String().
    StartsWith("hello").
    EndsWith("world").
    MaxLength(11)

  pattern2 := pattern.String().
    Contains("dni").
    Regex(regexp.MustCompile("night$"))

  pattern3 := pattern.String().
    MinLength(3)

  pattern4 := pattern.String()

  return pattern.NewMatcher[string](input).
    WithPattern(
      pattern1,
      func() string { return "pattern 1" },
    ).
    WithPattern(
      pattern2,
      func() string { return "pattern 2" },
    ).
    WithPattern(
      pattern3,
      func() string { return "pattern 3" },
    ).
    WithPattern(
      pattern4,
      func() string { return "pattern 4" },
    ).
    Otherwise(func() string { return "This is impossible" })
}

match("hello world") // "pattern 1"
match("goodnight")   // "pattern 2"
match("abc")         // "pattern 3"
match("ab")          // "pattern 4"
match("")            // "pattern 4"

Slice模式

func match(input []int) string {
  pattern1 := pattern.Slice[int]().
    Head(1).
    Tail(100)

  subPattern2 := pattern.Int().Between(75, 100)

  pattern2 := pattern.Slice[int]().
    Contains(25).
    Contains(50).
    ContainsPattern(subPattern2)

  subHeadPattern3 := pattern.Int().Gt(1000)
  subTailattern3 := pattern.Int().Gt(2500)
  pattern3 := pattern.Slice[int]().
    HeadPattern(subHeadPattern3).
    TailPattern(subTailattern3)

  return pattern.NewMatcher[string](input).
    WithPattern(
      pattern1,
      func() string { return "pattern 1" },
    ).
    WithPattern(
      pattern2,
      func() string { return "pattern 2" },
    ).
    WithPattern(
      pattern3,
      func() string { return "pattern 3" },
    ).
    Otherwise(func() string { return "No pattern matched" })
}

match([]int{1, 2, 3, 100})       // "pattern 1"
match([]int{2, 25, 85, 50})      // "pattern 2"
match([]int{1001, 25, 3, 25001}) // "pattern 3"

总结

go-pattern-match库为Golang提供了类似TypeScript风格的强大模式匹配功能,使得复杂的条件逻辑可以以更声明式和简洁的方式表达。通过使用各种内置模式和方法,开发者可以构建灵活且易于维护的模式匹配逻辑。


更多关于golang实现TypeScript风格模式匹配功能的插件库go-pattern-match的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang实现TypeScript风格模式匹配功能的插件库go-pattern-match的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


go-pattern-match: Golang实现TypeScript风格模式匹配

go-pattern-match是一个受TypeScript/Elm/Haskell启发的Golang模式匹配库,它提供了类似函数式编程语言的模式匹配能力。下面我将详细介绍它的使用方法和示例代码。

安装

go get github.com/alexpantyukhin/go-pattern-match

基本用法

1. 简单值匹配

import "github.com/alexpantyukhin/go-pattern-match"

result := match.Match(42).
    When(42, func() interface{} {
        return "the answer to life, the universe and everything"
    }).
    When(100, func() interface{} {
        return "one hundred"
    }).
    Otherwise(func() interface{} {
        return "something else"
    })

fmt.Println(result) // 输出: the answer to life, the universe and everything

2. 类型匹配

func process(input interface{}) string {
    return match.Match(input).
        When(match.String, func() interface{} {
            return "got a string: " + input.(string)
        }).
        When(match.Int, func() interface{} {
            return fmt.Sprintf("got an int: %d", input.(int))
        }).
        Otherwise(func() interface{} {
            return "unknown type"
        }).(string)
}

fmt.Println(process("hello")) // 输出: got a string: hello
fmt.Println(process(123))    // 输出: got an int: 123
fmt.Println(process(1.23))   // 输出: unknown type

3. 结构体匹配

type User struct {
    Name string
    Age  int
}

user := User{Name: "Alice", Age: 30}

result := match.Match(user).
    When(User{Name: "Alice"}, func() interface{} {
        return "Hello Alice!"
    }).
    When(User{Age: match.Gt(18)}, func() interface{} {
        return "Adult user"
    }).
    Otherwise(func() interface{} {
        return "Unknown user"
    })

fmt.Println(result) // 输出: Hello Alice!

4. 切片/数组匹配

func describeList(list []int) string {
    return match.Match(list).
        When([]int{}, func() interface{} {
            return "empty list"
        }).
        When([]int{match.Any}, func() interface{} {
            return "list with one element"
        }).
        When([]int{match.Any, match.Any}, func() interface{} {
            return "list with two elements"
        }).
        Otherwise(func() interface{} {
            return "list with many elements"
        }).(string)
}

fmt.Println(describeList([]int{}))        // empty list
fmt.Println(describeList([]int{1}))      // list with one element
fmt.Println(describeList([]int{1, 2}))   // list with two elements
fmt.Println(describeList([]int{1,2,3}))  // list with many elements

高级特性

1. 谓词匹配

result := match.Match(15).
    When(match.Gt(10), func() interface{} {
        return "greater than 10"
    }).
    When(match.Lt(5), func() interface{} {
        return "less than 5"
    }).
    Otherwise(func() interface{} {
        return "between 5 and 10"
    })

fmt.Println(result) // 输出: greater than 10

2. 自定义匹配器

func evenNumber() match.Matcher {
    return func(value interface{}) bool {
        if num, ok := value.(int); ok {
            return num%2 == 0
        }
        return false
    }
}

result := match.Match(4).
    When(evenNumber(), func() interface{} {
        return "even number"
    }).
    Otherwise(func() interface{} {
        return "odd number"
    })

fmt.Println(result) // 输出: even number

3. 解构匹配

type Point struct {
    X, Y int
}

p := Point{X: 1, Y: 2}

result := match.Match(p).
    When(Point{X: 0, Y: 0}, func() interface{} {
        return "origin"
    }).
    When(Point{X: match.Any, Y: 0}, func() interface{} {
        return "on x-axis"
    }).
    When(Point{X: 0, Y: match.Any}, func() interface{} {
        return "on y-axis"
    }).
    Otherwise(func() interface{} {
        return fmt.Sprintf("at (%d, %d)", p.X, p.Y)
    })

fmt.Println(result) // 输出: at (1, 2)

与TypeScript模式匹配的对比

虽然Golang没有TypeScript那样原生的模式匹配语法,但go-pattern-match提供了类似的表达能力:

  1. 值匹配:类似于TypeScript的switchif-else
  2. 类型保护:类似于TypeScript的类型守卫
  3. 解构模式:类似于TypeScript的对象解构匹配
  4. 谓词匹配:类似于TypeScript的自定义类型谓词

注意事项

  1. 由于Golang是静态类型语言,模式匹配不如TypeScript灵活
  2. 性能上会比原生条件语句稍慢,但大多数场景差异可以忽略
  3. 错误处理需要结合Golang的error机制

总结

go-pattern-match为Golang带来了接近TypeScript风格的模式匹配能力,虽然不如原生支持那么简洁,但在需要复杂条件分支时能显著提高代码可读性。它特别适合处理多种类型或复杂结构的条件分支场景。

对于更简单的条件判断,传统的if-elseswitch语句可能仍然是更好的选择,但在需要更强大的模式匹配能力时,go-pattern-match是一个很好的选择。

回到顶部