github.com/elves/elvish@v0.15.0/pkg/eval/builtin_fn_container.go (about)

     1  package eval
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"math"
     7  	"sort"
     8  
     9  	"github.com/elves/elvish/pkg/eval/errs"
    10  	"github.com/elves/elvish/pkg/eval/vals"
    11  	"github.com/elves/elvish/pkg/eval/vars"
    12  	"github.com/xiaq/persistent/hashmap"
    13  )
    14  
    15  // Sequence, list and maps.
    16  
    17  func init() {
    18  	addBuiltinFns(map[string]interface{}{
    19  		"ns": nsFn,
    20  
    21  		"make-map": makeMap,
    22  
    23  		"range":  rangeFn,
    24  		"repeat": repeat,
    25  
    26  		"assoc":  assoc,
    27  		"dissoc": dissoc,
    28  
    29  		"all": all,
    30  		"one": one,
    31  
    32  		"has-key":   hasKey,
    33  		"has-value": hasValue,
    34  
    35  		"take":  take,
    36  		"drop":  drop,
    37  		"count": count,
    38  
    39  		"keys": keys,
    40  
    41  		"order": order,
    42  	})
    43  }
    44  
    45  //elvdoc:fn ns
    46  //
    47  // ```elvish
    48  // ns $map
    49  // ```
    50  //
    51  // Constructs a namespace from `$map`, using the keys as variable names and the
    52  // values as their values. Examples:
    53  //
    54  // ```elvish-transcript
    55  // ~> n = (ns [&name=value])
    56  // ~> put $n[name]
    57  // ▶ value
    58  // ~> n: = (ns [&name=value])
    59  // ~> put $n:name
    60  // ▶ value
    61  // ```
    62  
    63  func nsFn(m hashmap.Map) (*Ns, error) {
    64  	nb := make(NsBuilder)
    65  	for it := m.Iterator(); it.HasElem(); it.Next() {
    66  		k, v := it.Elem()
    67  		kstring, ok := k.(string)
    68  		if !ok {
    69  			return nil, errs.BadValue{
    70  				What:  `key of argument of "ns"`,
    71  				Valid: "string", Actual: vals.Kind(k)}
    72  		}
    73  		nb[kstring] = vars.FromInit(v)
    74  	}
    75  	return nb.Ns(), nil
    76  }
    77  
    78  //elvdoc:fn make-map
    79  //
    80  // ```elvish
    81  // make-map $input?
    82  // ```
    83  //
    84  // Outputs a map from an input consisting of containers with two elements. The
    85  // first element of each container is used as the key, and the second element is
    86  // used as the value.
    87  //
    88  // If the same key appears multiple times, the last value is used.
    89  //
    90  // Examples:
    91  //
    92  // ```elvish-transcript
    93  // ~> make-map [[k v]]
    94  // ▶ [&k=v]
    95  // ~> make-map [[k v1] [k v2]]
    96  // ▶ [&k=v2]
    97  // ~> put [k1 v1] [k2 v2] | make-map
    98  // ▶ [&k1=v1 &k2=v2]
    99  // ~> put aA bB | make-map
   100  // ▶ [&a=A &b=B]
   101  // ```
   102  
   103  func makeMap(input Inputs) (vals.Map, error) {
   104  	m := vals.EmptyMap
   105  	var errMakeMap error
   106  	input(func(v interface{}) {
   107  		if errMakeMap != nil {
   108  			return
   109  		}
   110  		if !vals.CanIterate(v) {
   111  			errMakeMap = errs.BadValue{
   112  				What: "input to make-map", Valid: "iterable", Actual: vals.Kind(v)}
   113  			return
   114  		}
   115  		if l := vals.Len(v); l != 2 {
   116  			errMakeMap = errs.BadValue{
   117  				What: "input to make-map", Valid: "iterable with 2 elements",
   118  				Actual: fmt.Sprintf("%v with %v elements", vals.Kind(v), l)}
   119  			return
   120  		}
   121  		elems, err := vals.Collect(v)
   122  		if err != nil {
   123  			errMakeMap = err
   124  			return
   125  		}
   126  		if len(elems) != 2 {
   127  			errMakeMap = fmt.Errorf("internal bug: collected %v values", len(elems))
   128  			return
   129  		}
   130  		m = m.Assoc(elems[0], elems[1])
   131  	})
   132  	return m, errMakeMap
   133  }
   134  
   135  //elvdoc:fn range
   136  //
   137  // ```elvish
   138  // range &step=1 $low? $high
   139  // ```
   140  //
   141  // Output `$low`, `$low` + `$step`, ..., proceeding as long as smaller than
   142  // `$high`. If not given, `$low` defaults to 0.
   143  //
   144  // Examples:
   145  //
   146  // ```elvish-transcript
   147  // ~> range 4
   148  // ▶ 0
   149  // ▶ 1
   150  // ▶ 2
   151  // ▶ 3
   152  // ~> range 1 6 &step=2
   153  // ▶ 1
   154  // ▶ 3
   155  // ▶ 5
   156  // ```
   157  //
   158  // Beware floating point oddities:
   159  //
   160  // ```elvish-transcript
   161  // ~> range 0 0.8 &step=.1
   162  // ▶ 0
   163  // ▶ 0.1
   164  // ▶ 0.2
   165  // ▶ 0.30000000000000004
   166  // ▶ 0.4
   167  // ▶ 0.5
   168  // ▶ 0.6
   169  // ▶ 0.7
   170  // ▶ 0.7999999999999999
   171  // ```
   172  //
   173  // Etymology:
   174  // [Python](https://docs.python.org/3/library/functions.html#func-range).
   175  
   176  type rangeOpts struct{ Step float64 }
   177  
   178  func (o *rangeOpts) SetDefaultOptions() { o.Step = 1 }
   179  
   180  func rangeFn(fm *Frame, opts rangeOpts, args ...float64) error {
   181  	var lower, upper float64
   182  
   183  	switch len(args) {
   184  	case 1:
   185  		upper = args[0]
   186  	case 2:
   187  		lower, upper = args[0], args[1]
   188  	default:
   189  		return ErrArgs
   190  	}
   191  
   192  	out := fm.OutputChan()
   193  	for f := lower; f < upper; f += opts.Step {
   194  		out <- vals.FromGo(f)
   195  	}
   196  	return nil
   197  }
   198  
   199  //elvdoc:fn repeat
   200  //
   201  // ```elvish
   202  // repeat $n $value
   203  // ```
   204  //
   205  // Output `$value` for `$n` times. Example:
   206  //
   207  // ```elvish-transcript
   208  // ~> repeat 0 lorem
   209  // ~> repeat 4 NAN
   210  // ▶ NAN
   211  // ▶ NAN
   212  // ▶ NAN
   213  // ▶ NAN
   214  // ```
   215  //
   216  // Etymology: [Clojure](https://clojuredocs.org/clojure.core/repeat).
   217  
   218  func repeat(fm *Frame, n int, v interface{}) {
   219  	out := fm.OutputChan()
   220  	for i := 0; i < n; i++ {
   221  		out <- v
   222  	}
   223  }
   224  
   225  //elvdoc:fn assoc
   226  //
   227  // ```elvish
   228  // assoc $container $k $v
   229  // ```
   230  //
   231  // Output a slightly modified version of `$container`, such that its value at `$k`
   232  // is `$v`. Applies to both lists and to maps.
   233  //
   234  // When `$container` is a list, `$k` may be a negative index. However, slice is not
   235  // yet supported.
   236  //
   237  // ```elvish-transcript
   238  // ~> assoc [foo bar quux] 0 lorem
   239  // ▶ [lorem bar quux]
   240  // ~> assoc [foo bar quux] -1 ipsum
   241  // ▶ [foo bar ipsum]
   242  // ~> assoc [&k=v] k v2
   243  // ▶ [&k=v2]
   244  // ~> assoc [&k=v] k2 v2
   245  // ▶ [&k2=v2 &k=v]
   246  // ```
   247  //
   248  // Etymology: [Clojure](https://clojuredocs.org/clojure.core/assoc).
   249  //
   250  // @cf dissoc
   251  
   252  func assoc(a, k, v interface{}) (interface{}, error) {
   253  	return vals.Assoc(a, k, v)
   254  }
   255  
   256  var errCannotDissoc = errors.New("cannot dissoc")
   257  
   258  //elvdoc:fn dissoc
   259  //
   260  // ```elvish
   261  // dissoc $map $k
   262  // ```
   263  //
   264  // Output a slightly modified version of `$map`, with the key `$k` removed. If
   265  // `$map` does not contain `$k` as a key, the same map is returned.
   266  //
   267  // ```elvish-transcript
   268  // ~> dissoc [&foo=bar &lorem=ipsum] foo
   269  // ▶ [&lorem=ipsum]
   270  // ~> dissoc [&foo=bar &lorem=ipsum] k
   271  // ▶ [&lorem=ipsum &foo=bar]
   272  // ```
   273  //
   274  // @cf assoc
   275  
   276  func dissoc(a, k interface{}) (interface{}, error) {
   277  	a2 := vals.Dissoc(a, k)
   278  	if a2 == nil {
   279  		return nil, errCannotDissoc
   280  	}
   281  	return a2, nil
   282  }
   283  
   284  //elvdoc:fn all
   285  //
   286  // ```elvish
   287  // all $input-list?
   288  // ```
   289  //
   290  // Passes inputs to the output as is. Byte inputs into values, one per line.
   291  //
   292  // This is an identity function for commands with value outputs: `a | all` is
   293  // equivalent to `a` if it only outputs values.
   294  //
   295  // This function is useful for turning inputs into arguments, like:
   296  //
   297  // ```elvish-transcript
   298  // ~> use str
   299  // ~> put 'lorem,ipsum' | str:split , (all)
   300  // ▶ lorem
   301  // ▶ ipsum
   302  // ```
   303  //
   304  // Or capturing all inputs in a variable:
   305  //
   306  // ```elvish-transcript
   307  // ~> x = [(all)]
   308  // foo
   309  // bar
   310  // (Press ^D)
   311  // ~> put $x
   312  // ▶ [foo bar]
   313  // ```
   314  //
   315  // When given a list, it outputs all elements of the list:
   316  //
   317  // ```elvish-transcript
   318  // ~> all [foo bar]
   319  // ▶ foo
   320  // ▶ bar
   321  // ```
   322  //
   323  // @cf one
   324  
   325  func all(fm *Frame, inputs Inputs) {
   326  	out := fm.OutputChan()
   327  	inputs(func(v interface{}) { out <- v })
   328  }
   329  
   330  //elvdoc:fn one
   331  //
   332  // ```elvish
   333  // one $input-list?
   334  // ```
   335  //
   336  // Passes inputs to outputs, if there is only a single one. Otherwise raises an
   337  // exception.
   338  //
   339  // This function can be used in a similar way to [`all`](#all), but is a better
   340  // choice when you expect that there is exactly one output:
   341  //
   342  // @cf all
   343  
   344  func one(fm *Frame, inputs Inputs) error {
   345  	var val interface{}
   346  	n := 0
   347  	inputs(func(v interface{}) {
   348  		if n == 0 {
   349  			val = v
   350  		}
   351  		n++
   352  	})
   353  	if n == 1 {
   354  		fm.OutputChan() <- val
   355  		return nil
   356  	}
   357  	return fmt.Errorf("expect a single value, got %d", n)
   358  }
   359  
   360  //elvdoc:fn take
   361  //
   362  // ```elvish
   363  // take $n $input-list?
   364  // ```
   365  //
   366  // Retain the first `$n` input elements. If `$n` is larger than the number of input
   367  // elements, the entire input is retained. Examples:
   368  //
   369  // ```elvish-transcript
   370  // ~> take 3 [a b c d e]
   371  // ▶ a
   372  // ▶ b
   373  // ▶ c
   374  // ~> use str
   375  // ~> str:split ' ' 'how are you?' | take 1
   376  // ▶ how
   377  // ~> range 2 | take 10
   378  // ▶ 0
   379  // ▶ 1
   380  // ```
   381  //
   382  // Etymology: Haskell.
   383  
   384  func take(fm *Frame, n int, inputs Inputs) {
   385  	out := fm.OutputChan()
   386  	i := 0
   387  	inputs(func(v interface{}) {
   388  		if i < n {
   389  			out <- v
   390  		}
   391  		i++
   392  	})
   393  }
   394  
   395  //elvdoc:fn drop
   396  //
   397  // ```elvish
   398  // drop $n $input-list?
   399  // ```
   400  //
   401  // Drop the first `$n` elements of the input. If `$n` is larger than the number of
   402  // input elements, the entire input is dropped.
   403  //
   404  // Example:
   405  //
   406  // ```elvish-transcript
   407  // ~> drop 2 [a b c d e]
   408  // ▶ c
   409  // ▶ d
   410  // ▶ e
   411  // ~> use str
   412  // ~> str:split ' ' 'how are you?' | drop 1
   413  // ▶ are
   414  // ▶ 'you?'
   415  // ~> range 2 | drop 10
   416  // ```
   417  //
   418  // Etymology: Haskell.
   419  //
   420  // @cf take
   421  
   422  func drop(fm *Frame, n int, inputs Inputs) {
   423  	out := fm.OutputChan()
   424  	i := 0
   425  	inputs(func(v interface{}) {
   426  		if i >= n {
   427  			out <- v
   428  		}
   429  		i++
   430  	})
   431  }
   432  
   433  //elvdoc:fn has-value
   434  //
   435  // ```elvish
   436  // has-value $container $value
   437  // ```
   438  //
   439  // Determine whether `$value` is a value in `$container`.
   440  //
   441  // Examples, maps:
   442  //
   443  // ```elvish-transcript
   444  // ~> has-value [&k1=v1 &k2=v2] v1
   445  // ▶ $true
   446  // ~> has-value [&k1=v1 &k2=v2] k1
   447  // ▶ $false
   448  // ```
   449  //
   450  // Examples, lists:
   451  //
   452  // ```elvish-transcript
   453  // ~> has-value [v1 v2] v1
   454  // ▶ $true
   455  // ~> has-value [v1 v2] k1
   456  // ▶ $false
   457  // ```
   458  //
   459  // Examples, strings:
   460  //
   461  // ```elvish-transcript
   462  // ~> has-value ab b
   463  // ▶ $true
   464  // ~> has-value ab c
   465  // ▶ $false
   466  // ```
   467  
   468  func hasValue(container, value interface{}) (bool, error) {
   469  	switch container := container.(type) {
   470  	case hashmap.Map:
   471  		for it := container.Iterator(); it.HasElem(); it.Next() {
   472  			_, v := it.Elem()
   473  			if vals.Equal(v, value) {
   474  				return true, nil
   475  			}
   476  		}
   477  		return false, nil
   478  	default:
   479  		var found bool
   480  		err := vals.Iterate(container, func(v interface{}) bool {
   481  			found = (v == value)
   482  			return !found
   483  		})
   484  		return found, err
   485  	}
   486  }
   487  
   488  //elvdoc:fn has-key
   489  //
   490  // ```elvish
   491  // has-key $container $key
   492  // ```
   493  //
   494  // Determine whether `$key` is a key in `$container`. A key could be a map key or
   495  // an index on a list or string. This includes a range of indexes.
   496  //
   497  // Examples, maps:
   498  //
   499  // ```elvish-transcript
   500  // ~> has-key [&k1=v1 &k2=v2] k1
   501  // ▶ $true
   502  // ~> has-key [&k1=v1 &k2=v2] v1
   503  // ▶ $false
   504  // ```
   505  //
   506  // Examples, lists:
   507  //
   508  // ```elvish-transcript
   509  // ~> has-key [v1 v2] 0
   510  // ▶ $true
   511  // ~> has-key [v1 v2] 1
   512  // ▶ $true
   513  // ~> has-key [v1 v2] 2
   514  // ▶ $false
   515  // ~> has-key [v1 v2] 0:2
   516  // ▶ $true
   517  // ~> has-key [v1 v2] 0:3
   518  // ▶ $false
   519  // ```
   520  //
   521  // Examples, strings:
   522  //
   523  // ```elvish-transcript
   524  // ~> has-key ab 0
   525  // ▶ $true
   526  // ~> has-key ab 1
   527  // ▶ $true
   528  // ~> has-key ab 2
   529  // ▶ $false
   530  // ~> has-key ab 0:2
   531  // ▶ $true
   532  // ~> has-key ab 0:3
   533  // ▶ $false
   534  // ```
   535  
   536  func hasKey(container, key interface{}) bool {
   537  	return vals.HasKey(container, key)
   538  }
   539  
   540  //elvdoc:fn count
   541  //
   542  // ```elvish
   543  // count $input-list?
   544  // ```
   545  //
   546  // Count the number of inputs.
   547  //
   548  // Examples:
   549  //
   550  // ```elvish-transcript
   551  // ~> count lorem # count bytes in a string
   552  // ▶ 5
   553  // ~> count [lorem ipsum]
   554  // ▶ 2
   555  // ~> range 100 | count
   556  // ▶ 100
   557  // ~> seq 100 | count
   558  // ▶ 100
   559  // ```
   560  
   561  // The count implementation uses a custom varargs based implementation rather
   562  // than the more common `Inputs` API (see pkg/eval/go_fn.go) because this
   563  // allows the implementation to be O(1) for the common cases rather than O(n).
   564  func count(fm *Frame, args ...interface{}) (int, error) {
   565  	var n int
   566  	switch nargs := len(args); nargs {
   567  	case 0:
   568  		// Count inputs.
   569  		fm.IterateInputs(func(interface{}) {
   570  			n++
   571  		})
   572  	case 1:
   573  		// Get length of argument.
   574  		v := args[0]
   575  		if len := vals.Len(v); len >= 0 {
   576  			n = len
   577  		} else {
   578  			err := vals.Iterate(v, func(interface{}) bool {
   579  				n++
   580  				return true
   581  			})
   582  			if err != nil {
   583  				return 0, fmt.Errorf("cannot get length of a %s", vals.Kind(v))
   584  			}
   585  		}
   586  	default:
   587  		// The error matches what would be returned if the `Inputs` API was
   588  		// used. See GoFn.Call().
   589  		return 0, errs.ArityMismatch{
   590  			What: "arguments here", ValidLow: 0, ValidHigh: 1, Actual: nargs}
   591  	}
   592  	return n, nil
   593  }
   594  
   595  //elvdoc:fn keys
   596  //
   597  // ```elvish
   598  // keys $map
   599  // ```
   600  //
   601  // Put all keys of `$map` on the structured stdout.
   602  //
   603  // Example:
   604  //
   605  // ```elvish-transcript
   606  // ~> keys [&a=foo &b=bar &c=baz]
   607  // ▶ a
   608  // ▶ c
   609  // ▶ b
   610  // ```
   611  //
   612  // Note that there is no guaranteed order for the keys of a map.
   613  
   614  func keys(fm *Frame, v interface{}) error {
   615  	out := fm.OutputChan()
   616  	return vals.IterateKeys(v, func(k interface{}) bool {
   617  		out <- k
   618  		return true
   619  	})
   620  }
   621  
   622  //elvdoc:fn order
   623  //
   624  // ```elvish
   625  // order &reverse=$false $less-than=$nil $inputs?
   626  // ```
   627  //
   628  // Outputs the input values sorted in ascending order. The sort is guaranteed to
   629  // be [stable](https://en.wikipedia.org/wiki/Sorting_algorithm#Stability).
   630  //
   631  // The `&reverse` option, if true, reverses the order of output.
   632  //
   633  // The `&less-than` option, if given, establishes the ordering of the elements.
   634  // Its value should be a function that takes two arguments and outputs a single
   635  // boolean indicating whether the first argument is less than the second
   636  // argument. If the function throws an exception, `order` rethrows the exception
   637  // without outputting any value.
   638  //
   639  // If `&less-than` has value `$nil` (the default if not set), the following
   640  // comparison algorithm is used:
   641  //
   642  // - Numbers are compared numerically. For the sake of sorting, `NaN` is treated
   643  //   as smaller than all other numbers.
   644  //
   645  // - Strings are compared lexicographically by bytes, which is equivalent to
   646  //   comparing by codepoints under UTF-8.
   647  //
   648  // - Lists are compared lexicographically by elements, if the elements at the
   649  //   same positions are comparable.
   650  //
   651  // If the ordering between two elements are not defined by the conditions above,
   652  // no value is outputted and an exception is thrown.
   653  //
   654  // Examples:
   655  //
   656  // ```elvish-transcript
   657  // ~> put foo bar ipsum | order
   658  // ▶ bar
   659  // ▶ foo
   660  // ▶ ipsum
   661  // ~> order [(float64 10) (float64 1) (float64 5)]
   662  // ▶ (float64 1)
   663  // ▶ (float64 5)
   664  // ▶ (float64 10)
   665  // ~> order [[a b] [a] [b b] [a c]]
   666  // ▶ [a]
   667  // ▶ [a b]
   668  // ▶ [a c]
   669  // ▶ [b b]
   670  // ~> order &reverse [a c b]
   671  // ▶ c
   672  // ▶ b
   673  // ▶ a
   674  // ~> order &less-than=[a b]{ eq $a x } [l x o r x e x m]
   675  // ▶ x
   676  // ▶ x
   677  // ▶ x
   678  // ▶ l
   679  // ▶ o
   680  // ▶ r
   681  // ▶ e
   682  // ▶ m
   683  // ```
   684  //
   685  // Beware that strings that look like numbers are treated as strings, not
   686  // numbers. To sort strings as numbers, use an explicit `&less-than` option:
   687  //
   688  // ```elvish-transcript
   689  // ~> order [5 1 10]
   690  // ▶ 1
   691  // ▶ 10
   692  // ▶ 5
   693  // ~> order &less-than=[a b]{ < $a $b } [5 1 10]
   694  // ▶ 1
   695  // ▶ 5
   696  // ▶ 10
   697  // ```
   698  
   699  type orderOptions struct {
   700  	Reverse  bool
   701  	LessThan Callable
   702  }
   703  
   704  func (opt *orderOptions) SetDefaultOptions() {}
   705  
   706  // ErrUncomparable is raised by the order command when inputs contain
   707  // uncomparable values.
   708  var ErrUncomparable = errs.BadValue{
   709  	What:  `inputs to "order"`,
   710  	Valid: "comparable values", Actual: "uncomparable values"}
   711  
   712  func order(fm *Frame, opts orderOptions, inputs Inputs) error {
   713  	var values []interface{}
   714  	inputs(func(v interface{}) { values = append(values, v) })
   715  
   716  	var errSort error
   717  	var lessFn func(i, j int) bool
   718  	if opts.LessThan != nil {
   719  		lessFn = func(i, j int) bool {
   720  			if errSort != nil {
   721  				return true
   722  			}
   723  			var args []interface{}
   724  			if opts.Reverse {
   725  				args = []interface{}{values[j], values[i]}
   726  			} else {
   727  				args = []interface{}{values[i], values[j]}
   728  			}
   729  			outputs, err := fm.CaptureOutput(func(fm *Frame) error {
   730  				return opts.LessThan.Call(fm, args, NoOpts)
   731  			})
   732  			if err != nil {
   733  				errSort = err
   734  				return true
   735  			}
   736  			if len(outputs) != 1 {
   737  				errSort = errs.BadValue{
   738  					What:   "output of the &less-than callback",
   739  					Valid:  "a single boolean",
   740  					Actual: fmt.Sprintf("%d values", len(outputs))}
   741  				return true
   742  			}
   743  			if b, ok := outputs[0].(bool); ok {
   744  				return b
   745  			}
   746  			errSort = errs.BadValue{
   747  				What:  "output of the &less-than callback",
   748  				Valid: "boolean", Actual: vals.Kind(outputs[0])}
   749  			return true
   750  		}
   751  	} else {
   752  		// Use default comparison implemented by compare.
   753  		lessFn = func(i, j int) bool {
   754  			if errSort != nil {
   755  				return true
   756  			}
   757  			o := compare(values[i], values[j])
   758  			if o == uncomparable {
   759  				errSort = ErrUncomparable
   760  				return true
   761  			}
   762  			if opts.Reverse {
   763  				return o == more
   764  			}
   765  			return o == less
   766  		}
   767  	}
   768  
   769  	sort.SliceStable(values, lessFn)
   770  
   771  	if errSort != nil {
   772  		return errSort
   773  	}
   774  	for _, v := range values {
   775  		fm.OutputChan() <- v
   776  	}
   777  	return nil
   778  }
   779  
   780  type ordering uint8
   781  
   782  const (
   783  	less ordering = iota
   784  	equal
   785  	more
   786  	uncomparable
   787  )
   788  
   789  func compare(a, b interface{}) ordering {
   790  	switch a := a.(type) {
   791  	case float64:
   792  		if b, ok := b.(float64); ok {
   793  			switch {
   794  			case math.IsNaN(a):
   795  				if math.IsNaN(b) {
   796  					return equal
   797  				}
   798  				return less
   799  			case math.IsNaN(b):
   800  				return more
   801  			case a == b:
   802  				return equal
   803  			case a < b:
   804  				return less
   805  			default:
   806  				// a > b
   807  				return more
   808  			}
   809  		}
   810  	case string:
   811  		if b, ok := b.(string); ok {
   812  			switch {
   813  			case a == b:
   814  				return equal
   815  			case a < b:
   816  				return less
   817  			default:
   818  				// a > b
   819  				return more
   820  			}
   821  		}
   822  	case vals.List:
   823  		if b, ok := b.(vals.List); ok {
   824  			aIt := a.Iterator()
   825  			bIt := b.Iterator()
   826  			for aIt.HasElem() && bIt.HasElem() {
   827  				o := compare(aIt.Elem(), bIt.Elem())
   828  				if o != equal {
   829  					return o
   830  				}
   831  				aIt.Next()
   832  				bIt.Next()
   833  			}
   834  			switch {
   835  			case a.Len() == b.Len():
   836  				return equal
   837  			case a.Len() < b.Len():
   838  				return less
   839  			default:
   840  				// a.Len() > b.Len()
   841  				return more
   842  			}
   843  		}
   844  	}
   845  	return uncomparable
   846  }