The most important concept in all mathematics and/or programming languages is that of a function.

A function is a formula, or rule of correspondence that for each x in its domain , there is a corresponding value y assigned. And the set of corresponding y is called its range.

So, the feasibility covers two major points: the description capability of the domain and range; and the description capability of the formula or rule.

A domain or a range is a set consisting of specified type. Thanks to the new release 1.18, which includes an implementation of generic features, i.e. type parameters, the type system of golang finally goes forward for several paces.

The type system of golang is just as capable as other common programming languages. It can now express numeric, boolean, string, algebraic data types, and composites of which it expresses function types.

The algebraic data types and function types are composite types, to which go introduced type parameter.

Two common classes of algebraic types are product types and sum types. Golang represents product types in struct, but it lacks the facility to represent sum types. Lamely, we simulate sum types with a facility in go called interface or simulate it in product types with predefined distinguishing tags.

Below is an example.

We defined two packages, one defines option type and another defines result type.

option.go

package option

import "fmt"

const (
    some int= iota
    none
)

type Option[T any] struct {
    tag int;
    value *T;
}

func Return[T any](v T) Option[T] {
    return Option[T]{value: &v}
}

func None[T any]() Option[T] {
    return Option[T]{value: nil}
}

func Bind[T1 any, T2 any](o Option[T1], f func(v T1) Option[T2]) Option[T2] {
    if o.tag == some {
        return f(*o.value)
    } else if o.tag == none {
        return None[T2]()
    } else {
        panic(o)
    }
}

func Map[T1 any, T2 any](o Option[T1], f func(v T1) T2) Option[T2] {
    if o.tag == some {
        return Return(f(*o.value))
    } else if o.tag == none {
        return None[T2]()
    } else {
        panic(o)
    }
}

func (o Option[T])String() string {
    if o.tag == some {
        return "Some (" + fmt.Sprintf("%v", *o.value) + ")"
    } else if o.tag == none {
        return "None"
    } else {
        panic(o)
    }
}

result.go

package result

import "fmt"

const (
    ok int= iota
    error
)

type Result[TO any, TE any] struct {
    tag int;
    ok *TO;
    error *TE;
}

func Ok[TO any, TE any](o TO) Result[TO, TE] {
    return Result[TO, TE]{tag: ok, ok: &o, error:nil}
}

func Error[TO any, TE any](e TE) Result[TO, TE] {
    return Result[TO, TE]{tag: error, ok: nil, error:&e}
}

func Bind[TO1 any, TO2 any, TE any](o Result[TO1, TE], f func(v TO1) Result[TO2, TE]) Result[TO2, TE] {
    if o.tag == ok {
        return f(*o.ok)
    } else if o.tag == error {
        return Error[TO2, ](*o.error)
    } else {
        panic(o)
    }
}

func Map[TO1 any, TO2 any, TE any](o Result[TO1, TE], f func(v TO1) TO2) Result[TO2, TE] {
    if o.tag == ok {
        return Ok[TO2, TE](f(*o.ok))
    } else if o.tag == error {
        return Error[TO2, ](*o.error)
    } else {
        panic(o)
    }
}

func (r Result[TO, TE])String() string {
    if r.tag == ok {
        return "Ok (" + fmt.Sprintf("%v", *r.ok) + ")"
    } else if r.tag == error {
        return "Error (" + fmt.Sprintf("%v", *r.ok) + ")"
    } else {
        panic(r)
    }
}

The description capability of the domain and range is somewhat competent, let's have a look at the description capability of formula or rule.

Here is an example program:

package main

import (
    "fmt"
    "option"
    "result"
)

func main() {
    a:= result.Ok[Option[int], string](result.Return(1))
    b:= result.Bind(a, func(v option.Option[int]) result.Result[option.Option[int], string] {
        return result.Ok[option.Option[int], string](option.Bind(v, func (v int) option.Option[int] {
            return option.Return(v + 1)
        }))
    })
    fmt.Println(b)
}

Golang lacks the abilities to infer types globally, that's not a big deal. But the example code above reveals the fact that it even lacks the abilities of local type inference. We have to write everything explicitly in the body of a function even when it's unnecessary.

Then what about the feasibility of functional programming in golang? It's not so much to express logic in programming languages as to teach an idiot advanced mathematics.

- ZAN DoYe


Comments

comments powered by Disqus

© 2024 ZAN DoYe