type Food interface {
CanEatWith(interface {}) interface {}
}
type Tacos struct {
percentBeans int
percentHotSauce int
}
type Water struct {
volume int
quality int
}
type Meal struct {
iterms []interface {}
}
func (w *Water) CanEatWith(food interface {}) (interface {}) {
if food == nil {
return "Nil isn't edible"
}
switch x := food.(type) {
case Tacos:
// do some processing
return Meal{items:{x, w}}
case Meal:
// do some processing
return Meal{items:{x, w}}
default:
return "Unknown Arg"
}
}
Repeating that sort of structure for Tacos and also for processing the
return val of CanEatWIth().
It is simulating an overloaded function. I know it isn't going to
perform like an overloaded function, but is the overhead high enough
that I should avoid it during time critical code? I can't use the
if/else form because, x := food, this statement sets x's type to
interface since food was passed as and empty interface.
I am using this sequence incredibly often in my own packages, usually
combined with (chan interfaces {})'s as the input/output medium.
-Will
I guess I'll enum all my types as I would in other languages,
resulting in a code structure that switch's on enum values accessed
through a generic interface.
type TypeId interface {
Type() uint
}
type TypeId_ struct {
Id uint
}
func(t *TypeId_) Type() uint {
return t.Id
}
const (
IdTacos = iota;
IdWater;
)
type Tacos struct {
TypeId_
}
func generic(arg interface {}) interface {} {
x := arg.(TypeId)
switch x.Type() {
case IdTacos:
tacos := x.(Tacos)
//processing
return tacos
...etc
2 assertions, I'm happy with that.
a type assertion against a non-interface type is essentially one
pointer comparison; currently it's implemented in C and uses
three function calls. it'll probably be faster in the future, but
the cost is pretty small now.
see src/pkg/runtime/^·ifaceE2T for details.
against an interface, the first time between two types will be slower,
but subsequent tests won't be that many instructions.
see src/pkg/runtime/iface.c:/^itab
> func (w *Water) CanEatWith(food interface {}) (interface {}) {
> if food == nil {
> return "Nil isn't edible"
> }
this can go inside the switch, as below
> switch x := food.(type) {
case nil:
return "Nil isn't edible"
> case Tacos:
> // do some processing
> return Meal{items:{x, w}}
> case Meal:
> // do some processing
> return Meal{items:{x, w}}
> default:
> return "Unknown Arg"
> }
> }
[...]
> I can't use the
> if/else form because, x := food, this statement sets x's type to
> interface since food was passed as and empty interface.
i don't understand this.
Here is my working code example, using if statements isn't as pretty.
Still its quite nice and I almost perfer it to using overloaded
functions. The code is compact and concise.
package comm
import (
//"fmt"
i "ghthor/init"
//"runtime"
)
// (type)Init structs Allow for Simplified Construction
// especially with complex embedded types
type CommInit struct {
BufferLen int
}
type ComplexCommInit struct {
CommInit
NumChan int
}
// Defines a channel with embedded Buffer Length for easy cloning of an instance
type Comm struct {
i.InitVar
comm chan interface {}
}
// Constructor
func (c *Comm) Init(arg interface {}) (interface {}) {
// Call to embedded Init func that Checks for nil and reassigns arg
to i.Default
arg = c.InitVar.Init(arg)
var initArg CommInit
// Allow for Weird combos of arg for initialization
if x, ok := arg.(CommInit); ok {
initArg = x
} else if x, ok := arg.(int); ok {
initArg = CommInit{BufferLen:x}
} else {
initArg = CommInit{BufferLen:0}
}
// Initialize c *Comm
c.comm = make(chan interface {}, initArg.BufferLen)
c.InitArg = &initArg
return c
}
// Clean-up (Destructor)
func (c *Comm) Dispose(disposeComplete chan string) {
//TODO: Maybe have this do some reporting, like if there are things
left on the channel when it got closed
close(c.comm)
}
// These 2 struct's and func's Make the code a little ezier to read
down the road
type CommIn struct {
in chan interface {}
}
type CommOut struct {
out chan interface {}
}
func (c *Comm) AsIn() (in *CommIn) {
in = new(CommIn)
in.in = c.comm
return
}
func (c *Comm) AsOut() (out *CommOut) {
out = new(CommOut)
out.out = c.comm
return
}
// Comm that has multiple channels of communication
type ComplexComm struct {
i.InitVar
comm []*Comm
}
// Constructor
func (cc *ComplexComm) Init(arg interface {}) (interface {}) {
// nil Check
arg = cc.InitVar.Init(arg)
var initArg ComplexCommInit
if x, ok := arg.(ComplexCommInit); ok {
initArg = x
} else if x, ok := arg.(int); ok {
initArg = ComplexCommInit{NumChan:x}
} else if x, ok := arg.(CommInit); ok {
initArg = ComplexCommInit{NumChan:2, CommInit:x}
} else {
initArg = ComplexCommInit{NumChan:2} // CommInit.BufferLen defaults to 0
}
/* What I thought was that the statement
case ComplextCommInit:
was taking the place of
if x, ok := arg.(ComplexCommInit); ok
But now I understand that
switch arg.(type)
is just querying arg's declared type which is interface {}
I misunderstood Type Switch, for Type Assertion Switch
switch arg.(type) {
case ComplexCommInit:
initArg = arg.(ComplexCommInit)
case int:
initArg.NumChan = arg.(int)
initArg.BufferLen = 0
case CommInit:
initArg.NumChan = 2
initArg.BufferLen = arg.(CommInit).BufferLen
default:
initArg.NumChan = 2
initArg.BufferLen = 0
}
*/
cc.comm = make([]*Comm, initArg.NumChan)
for i := 0; i < len(cc.comm); i++ {
cc.comm[i] = new(Comm).Init(initArg.CommInit).(*Comm)
}
cc.InitArg = &initArg
return cc
}
// Destructor
func (cc *ComplexComm) Dispose(disposeComplete chan string) {
for i := 0; i < len(cc.comm); i++ {
cc.comm[i].Dispose(disposeComplete)
}
}
> /* What I thought was that the statement
> case ComplextCommInit:
> was taking the place of
> if x, ok := arg.(ComplexCommInit); ok
it is!
> But now I understand that
> switch arg.(type)
> is just querying arg's declared type which is interface {}
> I misunderstood Type Switch, for Type Assertion Switch
no, i think you understood just fine.
switch x := x.(type) {
case Foo:
print("foo")
default:
print("other")
}
is exactly equivalent to this, AFAIK:
if x, ok := x.(Foo); ok {
print("foo")
}else{
print("other")
}
import (
"fmt"
i "ghthor/init"
//"runtime"
//"ghthor/node"
)
type test struct {
i.InitVar
name string
}
func (t *test) Init(name interface {}) (interface {}) {
initVar := t.InitVar.Init(name)
switch initVar.(type) {
case *test:
t.name = name.(*test).name
case string:
t.name = initVar.(string)
default:
t.name = "Invalid Initialization Object"
}
t.InitArg = initVar
return t
}
func main() {
fmt.Printf("\n\n");
temp := new(test).Init("I've Been Awake to Long I guess").(*test)
taco := new(test).Init("Taco").(*test)
clone := new(test).Init(temp).(*test)
fmt.Println(temp.name, "\n", taco.name, "\n",clone.name)
}
:output:
I've Been Awake to Long I guess
Taco
I've Been Awake to Long I guess