- Notifications
You must be signed in to change notification settings - Fork1
An experimental implementation of 'try' operator for Go
License
rhysd/trygo
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
This is a translator of 'TryGo' as my experiment to see what happens if Go were havingtry()
function.Basic idea oftry()
came from Rust'stry!
macro (or?
operator).try()
handlesif err != nil
check implicitly.
This package provides a code translator from TryGo (Go withtry()
) to Go.
Go:
funcCreateFileInSubdir(subdir,filenamestring,content []byte)error {cwd,err:=os.Getwd()iferr!=nil {returnerr }iferr:=os.Mkdir(filepath.Join(cwd,subdir));err!=nil {returnerr }p:=filepath.Join(cwd,subdir,filename)f,err:=os.Create(p)iferr!=nil {returnerr }deferf.Close()if_,err:=f.Write(content);err!=nil {returnerr }fmt.Println("Created:",p)returnnil}
TryGo:
funcCreateFileInSubdir(subdir,filenamestring,content []byte)error {cwd:=try(os.Getwd())try(os.Mkdir(filepath.Join(cwd,subdir)))p:=filepath.Join(cwd,subdir,filename)f:=try(os.Create(p))deferf.Close()try(f.Write(content))fmt.Println("Created:",p)returnnil}
There is only one difference between Go and TryGo. Special magic functiontry()
is provided in TryGo.
try
looks function, but actually it is a special operator. It has variadic parameters and variadicreturn values. In terms of Go,try
looks like:
func try(ret... interface{}, err error) (... interface{})
Actuallytry()
is a set of macros which takes one function call and expands it to a code with errorcheck. It takes one function call as argument since Go only allows multiple values as return valuesof function call.
In following subsections,$zerovals
is expanded to zero-values of return values of the function.For example, whentry()
is used infunc () (int, error)
,$zerovals
will be0
. When it is usedinfunc () (*SomeStruct, SomeInterface, SomeStruct, error)
,$zerovals
will benil, nil, SomeStruct{}
.
Implementation:
- Definition statement
- Assignment statement
- Call statement
- Call Expression
$Vars := try($CallExpr)var $Vars = try($CallExpr)
Expanded to:
$Vars, err := $CallExprif err != nil { return $zerovals, err}var $Vars, err = $CallExprif err != nil { return $zerovals, err}
$Assignee = try($CallExpr)
Expanded to:
var err error$Assignee, err = $CallExprif err != nil { return $zerovals, err}
Assignment operationx op= y
(e.g.x += y
) is supported.
$Assignee op= try($CallExpr)
Expanded to:
$tmp, err := $CallExprif err != nil { return $zerovals, err}$Assignee op= $tmp
try($CallExpr)
Expanded to:
if $underscores, err := $CallExpr; err != nil { return err}
$underscores,
is a set of_
s which ignores all return values from$CallExpr
. For example, whencallingfunc() (int, error)
, it is expanded to_
. When callingfunc() (A, B, error)
intry()
,it is expanded to_, _
. When callingfunc() error
intry()
, it is expanded to an empty.
try()
call except for toplevel in block
1 + try($CallExpr)
Expanded to:
$tmp, err := $CallExprif err != nil { return $zerovals, err}1 + $tmp
This should allow nest. For example,
1 + try(Foo(try($CallExpr), arg))
$tmp1, err := $CallExprif err != nil { return $zerovals, err}$tmp2, err := Foo($tmp1, arg)if err != nil { return $zerovals, err}1 + $tmp2
The order of evaluation must be preserved. For example, whentry()
is used in a slice literal element,elements before the element must be calculated before theif err != nil
check of thetry()
.
For example,
ss := []string{"aaa", s1 + "x", try(f()), s2[:n]}
will be translated to
tmp1 := "aaa"tmp2 := s1 + "x"tmp3, err := f()if err != nil { return $zerovals, err}ss := []string{tmp1, tmp2, tmp3, s2[:n]}
try()
cannot take other than function call. For example,try(42)
is ill-formed.try()
is expanded to code includingreturn
. Using it outside functions is ill-formed.- When function called in
try()
invocation does not returnerror
as last of return values, it is ill-formed.
These ill-formed code should be detected by translator and it will raise an error.
Following code may look even better. At least I think so.
funcCreateFile(subdir,filenamestring,content []byte)error {cwd:=os.Getwd()? os.Mkdir(filepath.Join(cwd,subdir))?f:= os.Create(filepath.Join(cwd,subdir,filename))?deferf.Close() f.Write(content)?returnnil}
The reason why I adoptedtry()
function is that ...TODO
Download an executable binary fromrelease page (NOT YET).
To build from source:
$ go get -u github.com/rhysd/trygo/cmd/trygo
$ trygo -o {outpath} {inpaths}
{inpaths}
is a list of directory paths of Go packages you want to translate. The directories aretranslated recursively. For example, whendir
is passed and there are 2 packagesdir
anddir/nested
,both packages will be translated.
{outpath}
is a directory path where translated Go packages are put. For example, whendir
is specifiedas{inpaths}
andout
is specified as{outpath}
,dir/**
packages are translated asout/dir/**
.