code: func UserTypes(dt DataType) map[string]*UserTypeDefinition {
  	collect := func(types map[string]*UserTypeDefinition) func(*AttributeDefinition) error {
  		return func(at *AttributeDefinition) error {
  			if u, ok := at.Type.(*UserTypeDefinition); ok {
  				types[u.TypeName] = u
  			} else if m, ok := at.Type.(*MediaTypeDefinition); ok {
  				types[m.TypeName] = m.UserTypeDefinition
  			}
  			return nil
  		}
  	}
  	switch actual := dt.(type) {
  	case Primitive:
  		return nil
  	case *Array:
  		return UserTypes(actual.ElemType.Type)
  	case *Hash:
  		ktypes := UserTypes(actual.KeyType.Type)
  		vtypes := UserTypes(actual.ElemType.Type)
  		if vtypes == nil {
  			return ktypes
  		}
  		for n, ut := range ktypes {
  			vtypes[n] = ut
  		}
  		return vtypes
  	case Object:
  		types := make(map[string]*UserTypeDefinition)
  		for _, att := range actual {
  			att.Walk(collect(types))
  		}
  		if len(types) == 0 {
  			return nil
  		}
  		return types
  	case *UserTypeDefinition:
  		types := map[string]*UserTypeDefinition{actual.TypeName: actual}
  		actual.Walk(collect(types))
  		return types
  	case *MediaTypeDefinition:
  		types := map[string]*UserTypeDefinition{actual.TypeName: actual.UserTypeDefinition}
  		actual.Walk(collect(types))
  		return types
  	default:
  		panic("unknown type") // bug
  	}
  }
docs: // UserTypes traverses the data type recursively and collects all the user types used to
  // define it. The returned map is indexed by type name.