func (val Value) Equals(other Value) Value {
	switch {
	case !val.IsKnown() && !other.IsKnown():
		return UnknownVal(Bool)
	case val.IsKnown() && !other.IsKnown():
		switch {
		case val.IsNull(), other.ty.HasDynamicTypes():
			return UnknownVal(Bool)
		case !val.ty.Equals(other.ty):
			return False
		default:
			return UnknownVal(Bool)
		}
	case other.IsKnown() && !val.IsKnown():
		switch {
		case other.IsNull(), val.ty.HasDynamicTypes():
			return UnknownVal(Bool)
		case !other.ty.Equals(val.ty):
			return False
		default:
			return UnknownVal(Bool)
		}
	}
	switch {
	case val.IsNull() && other.IsNull():
		return BoolVal(true)
	case val.IsNull() || other.IsNull():
		return BoolVal(false)
	}
	if val.ty.HasDynamicTypes() || other.ty.HasDynamicTypes() {
		return UnknownVal(Bool)
	}
	if !val.ty.Equals(other.ty) {
		return BoolVal(false)
	}
	ty := val.ty
	result := false
	switch {
	case ty == Number:
		result = val.v.(*big.Float).Cmp(other.v.(*big.Float)) == 0
	case ty == Bool:
		result = val.v.(bool) == other.v.(bool)
	case ty == String:
		result = val.v.(string) == other.v.(string)
	case ty.IsObjectType():
		oty := ty.typeImpl.(typeObject)
		result = true
		for attr, aty := range oty.AttrTypes {
			lhs := Value{
				ty: aty,
				v:  val.v.(map[string]interface{})[attr],
			}
			rhs := Value{
				ty: aty,
				v:  other.v.(map[string]interface{})[attr],
			}
			eq := lhs.Equals(rhs)
			if !eq.IsKnown() {
				return UnknownVal(Bool)
			}
			if eq.False() {
				result = false
				break
			}
		}
	case ty.IsTupleType():
		tty := ty.typeImpl.(typeTuple)
		result = true
		for i, ety := range tty.ElemTypes {
			lhs := Value{
				ty: ety,
				v:  val.v.([]interface{})[i],
			}
			rhs := Value{
				ty: ety,
				v:  other.v.([]interface{})[i],
			}
			eq := lhs.Equals(rhs)
			if !eq.IsKnown() {
				return UnknownVal(Bool)
			}
			if eq.False() {
				result = false
				break
			}
		}
	case ty.IsListType():
		ety := ty.typeImpl.(typeList).ElementTypeT
		if len(val.v.([]interface{})) == len(other.v.([]interface{})) {
			result = true
			for i := range val.v.([]interface{}) {
				lhs := Value{
					ty: ety,
					v:  val.v.([]interface{})[i],
				}
				rhs := Value{
					ty: ety,
					v:  other.v.([]interface{})[i],
				}
				eq := lhs.Equals(rhs)
				if !eq.IsKnown() {
					return UnknownVal(Bool)
				}
				if eq.False() {
					result = false
					break
				}
			}
		}
	case ty.IsSetType():
		s1 := val.v.(set.Set)
		s2 := other.v.(set.Set)
		equal := true
		s1.EachValue(func(v interface{}) {
			if !s2.Has(v) {
				equal = false
			}
		})
		s2.EachValue(func(v interface{}) {
			if !s1.Has(v) {
				equal = false
			}
		})
		result = equal
	case ty.IsMapType():
		ety := ty.typeImpl.(typeMap).ElementTypeT
		if len(val.v.(map[string]interface{})) == len(other.v.(map[string]interface{})) {
			result = true
			for k := range val.v.(map[string]interface{}) {
				if _, ok := other.v.(map[string]interface{})[k]; !ok {
					result = false
					break
				}
				lhs := Value{
					ty: ety,
					v:  val.v.(map[string]interface{})[k],
				}
				rhs := Value{
					ty: ety,
					v:  other.v.(map[string]interface{})[k],
				}
				eq := lhs.Equals(rhs)
				if !eq.IsKnown() {
					return UnknownVal(Bool)
				}
				if eq.False() {
					result = false
					break
				}
			}
		}
	case ty.IsCapsuleType():
		return BoolVal(val.v == other.v)
	default:
		panic(fmt.Errorf("unsupported value type %#v in Equals", ty))
	}
	return BoolVal(result)
}
Equals returns True if the receiver and the given other value have the
same type and are exactly equal in value.

As a special case, two null values are always equal regardless of type.

The usual short-circuit rules apply, so the result will be unknown if
either of the given values are.

Use RawEquals to compare if two values are equal *ignoring* the
short-circuit rules and the exception for null values.
package cty
import (
	"fmt"
	"math/big"
	"reflect"
	"github.com/zclconf/go-cty/cty/set"
)
func (val Value) GoString() string {
	if val == NilVal {
		return "cty.NilVal"
	}
	if val.IsNull() {
		return fmt.Sprintf("cty.NullVal(%#v)", val.ty)
	}
	if val == DynamicVal {  
		return "cty.DynamicVal"
	}
	if !val.IsKnown() {
		return fmt.Sprintf("cty.UnknownVal(%#v)", val.ty)
	}
	switch val.ty {
	case Bool:
		if val.v.(bool) {
			return "cty.True"
		}
		return "cty.False"
	case Number:
		fv := val.v.(*big.Float)
		if fv.IsInt() {
			return fmt.Sprintf("cty.NumberIntVal(%#v)", fv)
		}
		if rfv, accuracy := fv.Float64(); accuracy == big.Exact {
			return fmt.Sprintf("cty.NumberFloatVal(%#v)", rfv)
		}
		return fmt.Sprintf("cty.MustParseNumberVal(%q)", fv.Text('f', -1))
	case String:
		return fmt.Sprintf("cty.StringVal(%#v)", val.v)
	}
	switch {
	case val.ty.IsSetType():
		vals := val.AsValueSlice()
		if len(vals) == 0 {
			return fmt.Sprintf("cty.SetValEmpty(%#v)", val.ty.ElementType())
		}
		return fmt.Sprintf("cty.SetVal(%#v)", vals)
	case val.ty.IsListType():
		vals := val.AsValueSlice()
		if len(vals) == 0 {
			return fmt.Sprintf("cty.ListValEmpty(%#v)", val.ty.ElementType())
		}
		return fmt.Sprintf("cty.ListVal(%#v)", vals)
	case val.ty.IsMapType():
		vals := val.AsValueMap()
		if len(vals) == 0 {
			return fmt.Sprintf("cty.MapValEmpty(%#v)", val.ty.ElementType())
		}
		return fmt.Sprintf("cty.MapVal(%#v)", vals)
	case val.ty.IsTupleType():
		if val.ty.Equals(EmptyTuple) {
			return "cty.EmptyTupleVal"
		}
		vals := val.AsValueSlice()
		return fmt.Sprintf("cty.TupleVal(%#v)", vals)
	case val.ty.IsObjectType():
		if val.ty.Equals(EmptyObject) {
			return "cty.EmptyObjectVal"
		}
		vals := val.AsValueMap()
		return fmt.Sprintf("cty.ObjectVal(%#v)", vals)
	case val.ty.IsCapsuleType():
		return fmt.Sprintf("cty.CapsuleVal(%#v, %#v)", val.ty, val.v)
	}
	return fmt.Sprintf("cty.Value{ty: %#v, v: %#v}", val.ty, val.v)
}