glycerine/zygomys

While loop

japanoise opened this issue · 3 comments

It'd be rad to have a while loop construct. I've had to hack it in myself, using this:

func truthyResult(env *zygo.Zlisp, fun *zygo.SexpFunction) (bool, error) {
	res, err := env.Apply(fun, []zygo.Sexp{})
	if err != nil {
		return false, err
	}
	switch rt := res.(type) {
	case *zygo.SexpBool:
		return rt.Val, nil
	case *zygo.SexpSentinel:
		return false, nil
	default:
		return true, nil
	}
}

// While hacks a while loop into zygomys
func While(env *zygo.Zlisp, name string, args []zygo.Sexp) (zygo.Sexp, error) {
	if len(args) != 2 {
		return zygo.SexpNull, errors.New("Syntax: (while cond body)")
	}
	var cond, body *zygo.SexpFunction
	switch st := args[0].(type) {
	case *zygo.SexpFunction:
		cond = st
	default:
		return zygo.SexpNull, errors.New("While loop takes two functions")
	}
	switch st := args[1].(type) {
	case *zygo.SexpFunction:
		body = st
	default:
		return zygo.SexpNull, errors.New("While loop takes two functions")
	}
	result, err := truthyResult(env, cond)
	if err != nil {
		return zygo.SexpNull, err
	}
	for result {
		_, err := env.Apply(body, []zygo.Sexp{})
		result, err = truthyResult(env, cond)
		if err != nil {
			return zygo.SexpNull, err
		}
	}
	return zygo.SexpNull, err
}

Is there a better way? Or would this require extending the language?

I'd like to be able to do:

(while (== true cond)
  (some)
  (body))

Hi @japanoise that would be pretty cool. Just add a couple of correctness tests to the test suite and file a pull request, and we'll put it in.

Well, currently the API is too clumsy. I wouldn't put it in as-is.

You have to do e.g.

(while (fn [] (==cond true) (fn [] (body goes here))

I was wondering if you had a macro hack or something to make it nicer.

The proper way would be to add it to the built in codegen, alongside the for builtin. You could probably just copy and modify the for Generator.GenerateForLoop code in generator.go:817 to be like Go and only need the middle test.