From: sgf Date: Mon, 26 Aug 2024 17:45:47 +0000 (+0300) Subject: Reverse slice in go using CPS with generics. X-Git-Url: https://gitweb.sgf-dma.tk/?a=commitdiff_plain;h=29440602a921651051366cfac13bb49c13e3f0db;p=go.git Reverse slice in go using CPS with generics. --- diff --git a/revCps/rev.go b/revCps/rev.go index 5818c45..d602ccc 100644 --- a/revCps/rev.go +++ b/revCps/rev.go @@ -3,7 +3,7 @@ package main import "fmt" -// Haskell: data R a = R (R a) | E {unE :: a} +// Haskell: data R a = R (R a) | E a type R func() (R, []int) // Haskell: Cont (R [a]) [a] @@ -25,10 +25,10 @@ func step(x int, xs []int, k Cont) (R, []int) { // Haskell: In fact, this is '>>= \zs -> shift (f zs)' in foldr, which is // hidden behind 'm'. For reference, haskell shift implementation: // shift :: ((a -> r) -> Cont r r) -> Cont r a -// shift f = Cont $ \k -> runCont (f k) id +// shift g = Cont $ \k -> runCont (g k) id func shift(f func([]int, Cont) (R, []int), k Cont) Cont { return func (xs []int) (R, []int) { - return f(xs, k) // Haskell's shift: runCont (f k) id + return f(xs, k) // Haskell's shift: runCont (g k) id } } @@ -53,8 +53,8 @@ func reverseCps(xs0 []int) (R, []int) { return nil, []int{} // return . E } -func getResult(f R, zs []int) []int { - for ; zs == nil; f, zs = f() { // Haskell: fix run +func runR(f R, zs []int) []int { + for ; f != nil; f, zs = f() { // Haskell: fix run fmt.Printf("Iterate..\n") } return zs @@ -62,6 +62,6 @@ func getResult(f R, zs []int) []int { func main() { xs := []int{1, 2, 3, 4} - fmt.Printf("%v\n", getResult(reverseCps(xs))) + fmt.Printf("%v\n", runR(reverseCps(xs))) } diff --git a/revCps/revGen.go b/revCps/revGen.go new file mode 100644 index 0000000..0367609 --- /dev/null +++ b/revCps/revGen.go @@ -0,0 +1,69 @@ + +package main + +import "fmt" + +// Haskell: data R a = R (R a) | E a +type R [Result any] func() (R[Result], Result) + +// Haskell: Cont (R Result) T +type Cont [T any, Result any] func(T) (R[Result], Result) + +// Type of function in shift, just a shortcut. See note below about this +// 'shift' and Haskell's 'shift' differences. +type shiftFunc [T any, Result any] func(T, Cont[T, Result]) (R[Result], Result) + +// Haskell: In fact, this is '>>= \zs -> shift (f zs)' in foldr, which is +// hidden behind 'm'. For reference, haskell shift implementation: +// shift :: ((a -> r) -> Cont r r) -> Cont r a +// shift g = Cont $ \k -> runCont (g k) id +func shift[T any, Result any](f shiftFunc[T, Result], k Cont[T, Result]) Cont[T, Result] { + return func (xs T) (R[Result], Result) { + return f(xs, k) // Haskell's shift: runCont (g k) id + } +} + +func runR[Result any](f R[Result], zs Result) Result { + for ; f != nil; f, zs = f() { // Haskell: fix run + fmt.Printf("Iterate..\n") + } + return zs +} + +// step builds reverse slice in xs, but the final result is string +// representation of reverse slice. Well, this is just for more easily +// distinguishing result type from type passed along the monadic chain. +func step(x int, xs []int, k Cont[[]int, string]) (R[string], string) { + fmt.Printf("Got x = %v, xs = %v, k = %v\n", x, xs, k) + xs = append(xs, x) + if k != nil { // Preserve nil. + // Haskell: R (k (x : xs)) + t := func() (R[string], string) { // t is thunk. + return k(xs) + } + return t, "" + } + return nil, fmt.Sprintf("%v", xs) +} + +func reverseCps(xs0 []int) (R[string], string) { + // Build reverse slice into slice in monadic chain, but return result as a + // its string representation. + var m Cont[[]int, string] // Haskell: Cont (R [a]) [a] + for _, x := range xs0 { // Haskell: foldr .. xs + stepX := func (xs []int, k Cont[[]int, string]) (R[string], string) { return step(x, xs, k) } // Haskell: step x + m = shift(stepX, m) // Haskell: \zs -> m =<< (shift (step x zs)) + } + + if m != nil { + // Unwrap Cont. + return m(nil) // flip runCont id + } + return nil, "" // return . E +} + +func main() { + xs := []int{1, 2, 3, 4} + fmt.Printf("%v\n", runR(reverseCps(xs))) +} +