jennifer icon indicating copy to clipboard operation
jennifer copied to clipboard

Inconsistent behaviour of Lit with slices

Open kendfss opened this issue 1 year ago • 0 comments

This could well be better placed in the Go issue tracker, sorry if so.

// gen.go
package main

import (
	"fmt"

	. "github.com/dave/jennifer/jen"
)

func main() {
	c := Id("perms").Op(":=").Map(String()).String().Values(DictFunc(func(d Dict) {
		for _, perm := range [][]int{{0, 1}, {1, 0}} {
			d[Lit(fmt.Sprint(perm))] = Lit(perm)
			d[Lit(fmt.Sprint(perm))] = Index().Int().Values(spell(Cast(Lit, Anify(perm)))...)
		}
	}))
	fmt.Printf("%#v", c)
}

// Cast behaves like "map" methods/functions in functional languages
func Cast[E, V any](f func(E) V, s []E) []V {
	out := make([]V, len(s))
	for i, e := range s {
		out[i] = f(e)
	}
	return out
}

// Convert a slice of any type into a []interface{}
func Anify[E any](slice []E) []any {
	out := make([]any, len(slice))
	for i := range out {
		out[i] = slice[i]
	}
	return out
}

// spell converts a slice of Statement pointers into a slice of Code objects
func spell(arg []*Statement) []Code {
	out := make([]Code, len(arg))
	for i, e := range arg {
		out[i] = e
	}
	return out
}

playground

If I run the code I, usually (see the bullet), get the following output:

perms := map[string]string{
        "[0 1]": []int{0, 1},
        "[0 1]": []int{0, 1},
        "[1 0]": []int{1, 0},
        "[1 0]": []int{1, 0},
}
  • even less consistently in the playground but it still happens every 2nd run, the other half of outputs is the same as the next step
  • always the same on my home machine

If I comment out the line d[Lit(fmt.Sprint(perm))] = Index().Int().Values(spell(Cast(Lit, Anify(perm)))...), I get:

%!v(PANIC=GoString method: unsupported type for literal: []int)
  • which may also be output in playground with both lines uncommented
  • I don't think this should happen at all if it can actually print a slice literal, maybe this is a language issue, but maybe there's something worth exploiting here?

this part is a little less relevant, mainly just shows If I, instead, comment out the line d[Lit(fmt.Sprint(perm))] = Lit(perm), I get:

perms := map[string]string{
        "[0 1]": []int{0, 1},
        "[1 0]": []int{1, 0},
}

Which is what I wanted all along


May I also suggest adding conversion functions func([]T)[]interface{} and func([]*Statement)[]Code to the library (assuming they aren't already there)

kendfss avatar Oct 28 '24 03:10 kendfss