mattn/gom

Why the ruby syntax?

voronoipotato opened this issue · 41 comments

I know this is a design decision, but I feel that it should be more like go. It seems less intuitive to have something written in go, for go, that looks like ruby simply because it was inspired by a ruby tool.

Could use YAML ?

YAML might work actually. I'm not seeing any standard YAML go library but we could use libYAML.

I think, yaml is good but yaml isn't DSL which people can judge the conditions as human readable.

We could literally just write it like go code. The problem with writing it like ruby is it makes an assumption that you know and are familiar with ruby code.

You might be right, but lets see what we can do with the README example.

gom 'github.com/mattn/go-runewidth', :tag => 'go1'
gom 'github.com/mattn/go-scan', :commit => 'ecb144fb1f2848a24ebfdadf8e64380406d87206'
gom 'github.com/daviddengcn/go-colortext'
gom 'github.com/mattn/go-ole', :goos => 'windows'

Could be written:

- gom: github.com/mattn/go-runewidth
  tag: go1

- gom: github.com/mattn/go-scan
  commit: ecb144fb1f2848a24ebfdadf8e64380406d87206

- gom: github.com/daviddengcn/go-colortext

- gom: github.com/mattn/go-ole
  goos: windows

Personally I'd probably use more natural terms, like uri or dep instead of gom, and so forth, but that's a minor point.

If there is some concern that additional top-level info will be required one day, then adding a top-level mapping would do the trick. e.g.

dependencies:
- gom: ...

Of course, thinking about it a bit more, you are using your own parser currently, so you could do it any way you wanted, which really does make the use of ruby symbols (e.g. :tag) seem a bit odd.

What would the go code version look like?

How about this in yaml?

group :production, :development do
  gom 'github.com/mattn/go-ole', :goos => 'windows'
end

There's also the encoding/json built in, which would definitely eliminate potential errors. However some readability would be lost. Then there's this https://github.com/emilsjolander/goson

I don't think ruby's syntax is best for gom. but I think yaml is not better than ruby's syntax.

I agree. If you need that level of expressiveness then it's either verbose XML or a DSL. If you want to do it in the most Go-ish way possible I suspect it would have to be by approximating functions and function chaining.

Gom("github.com/mattn/go-runewidth").Tag("go1")
Gom("github.com/mattn/go-scan").Commit("ecb144fb1f2848a24ebfdadf8e64380406d87206")
Gom("github.com/daviddengcn/go-colortext")
Gom("github.com/mattn/go-ole").Os("windows")

Group("production", "development") { 
  Gom("github.com/mattn/go-ole").Os("windows")
}

What about a JSON file like Bower uses it?

Everybody knows JSON, it's easily extendable and go hast built-in support for it.

A definition file could look like this:

{
    dependencies: [
        {
            gom: "github.com/mattn/go-runewidth",
            tag: "go1"
        },
        {
            gom: "github.com/mattn/go-scan",
            commit: "ecb144fb1f2848a24ebfdadf8e64380406d87206"
        },
        {
            gom: "github.com/daviddengcn/go-colortext"
        },
        {
            gom: "github.com/mattn/go-ole",
            goos: "windows"
        },        
    ]
}

I don't see any advantage to using JSON over YAML.

BTW http://theplant.github.io/pak/

@trans pak seems not have condition like goos or production/development.

I cam across QML for Go today. After looking at the QML, it reminded me of this issue b/c the QML looks a bit like Go (sort of like a GO and CSS hybrid). Given that, Gom could do something like:

Gom {
  uri: github.com/mattn/go-runewidth 
  tag: go1 
}

Gom { 
  uri: github.com/mattn/go-scan
  commit: ecb144fb1f2848a24ebfdadf8e64380406d87206
}

Gom { uri: github.com/daviddengcn/go-colortext }

Gom {
  uri: github.com/mattn/go-ole
  os: windows
}

Group production, development { 
  Gom { 
    uri: github.com/mattn/go-ole
    os: windows
  }
}

It looks more verbose, but entries could be condensed, e.g.

  Gom { uri: github.com/mattn/go-ole  os: windows }

as long as there are no spaces involved, in which case quotes would be necessary anyway I think. Either that or ; could used to separate entries on the same line.

I think this is better then the Ruby syntax in that it has a fairly Go look and feel.

@mattn BTW, per your question about expressing the following in YAML.

group :production, :development do
  gom 'github.com/mattn/go-ole', :goos => 'windows'
end

I don't think there is really a particularly better way then just adding groups to each entry, e.g.

- gom: github.com/mattn/go-ole
  group: production, development
  os: windows

Redundant but simple.

QML is too fat for gom.

BTW, per your question about expressing the following in YAML.

How do you write below in YAML? two section?

group :production, :development do
  gom 'github.com/daviddengcn/go-colortext-for-windows', :goos => [:windows]
  gom 'github.com/daviddengcn/go-colortext-for-linux', :goos => [:linux]
end

@mattn Oh no, I wasn't suggesting you actually use QML! That was just my inspiration for the notation. I meant that the current parser could be modified to parse a syntax that was similar to QML's. Actually, it is very similar to CSS too, if that helps give a different perspective on it.

As for the YAML, you would do it like so:

- gom: github.com/daviddengcn/go-colortext-for-windows
  goos: [windows]
  group: [production, development]

- gom: github.com/daviddengcn/go-colortext-for-linux
  goos: [linux]
  group: [production, development]

Why goos and not just os, btw?

also I think dep instead of gom might be more descriptive.

+1 for JSON, since it's the only notation language supported natively by Go

Go's JSON doesn't allow to write comment. It's too strictly.

@mattn what about if we using newer Ruby hash syntax:

gom 'github.com/mattn/go-scan', commit: 'ecb144fb1f2848a24ebfdadf8e64380406d87206'

Optionally, we can also drop "gom" keyword, so it will be:

github.com/mattn/go-scan, commit: 'ecb144fb1f2848a24ebfdadf8e64380406d87206'

And there is no symbol for the group:

group "production", "development" {
    github.com/mattn/go-scan, commit: 'ecb144fb1f2848a24ebfdadf8e64380406d87206'
{

or

group "production", "development":
    github.com/mattn/go-scan, commit: 'ecb144fb1f2848a24ebfdadf8e64380406d87206'

We can also define group as part of the line:

github.com/mattn/go-scan, commit: 'ecb144fb1f2848a24ebfdadf8e64380406d87206', group: "production, development"

What do you think?

The problem with using ruby syntax is there's no reason why any Go developer would be familiar with it. You lose all advantages of a DSL when you go outside the domain. We could also use lisp, or perl, or even haskell but those aren't going to be helpful but to a small subset of developers.

Surprised that no one gave a feedback on @trans Go-ish proposal on #5 (comment)
I'd change the Group curly braces by parentheses though, so it could be feasible to implement and idiomatic, too:

Gom("github.com/mattn/go-runewidth").Tag("go1")
Gom("github.com/mattn/go-scan").Commit("ecb144fb1f2848a24ebfdadf8e64380406d87206")
Gom("github.com/daviddengcn/go-colortext")
Gom("github.com/mattn/go-ole").Os("windows")

Group("production", "development") ( 
  Gom("github.com/mattn/go-ole").Os("windows")
)

In other hand, I know you guys probably see rubygems and bundle as the way to follow, but I don't really like having so similar specs and naming to Ruby. Thus, I'd just call .gom to the current Gomfile: cleaner, not intrusive and not intented to be a copy of anything.

EDIT

Another approach, totally OOP-ish:

Gom("github.com/mattn/go-runewidth").Tag("go1")
Gom("github.com/mattn/go-scan").Commit("ecb144fb1f2848a24ebfdadf8e64380406d87206")
Gom("github.com/daviddengcn/go-colortext")
Gom("github.com/mattn/go-ole").Os("windows")

Group("production", "development").Gom("github.com/mattn/go-ole").Os("windows")

+1 for yaml

I like the go function chaining style

Just occurred to me that method chaining across newlines should be possible. So it could be written:

Gom("github.com/mattn/go-runewidth").
  Tag("go1")

Gom("github.com/mattn/go-scan").
  Commit("ecb144fb1f2848a24ebfdadf8e64380406d87206")

Gom("github.com/daviddengcn/go-colortext")

Gom("github.com/mattn/go-ole").
  Os("windows")

Group("production", "development").
  Gom("github.com/mattn/go-ole").
    Os("windows")

I wrote example to use go syntax, hmm go is awsome but seems not useful to define DSL. . should be located at end of line. And compiler doesn't allow to locate . at leading of the line. gofmt breaks nesting for third line.

package main

import (
    "fmt"
)

var goms []*TGom

type TGroup struct {
    envs []string
}

type TGom struct {
    name   string
    commit string
    envs   []string
    os     []string
}

func Group(envs ...string) *TGroup {
    return &TGroup{envs}
}

func (group *TGroup) Gom(name string) *TGom {
    gom := &TGom{name, "", group.envs, []string{}}
    goms = append(goms, gom)
    return gom
}

func Gom(name string) *TGom {
    gom := &TGom{name, "", []string{}, []string{}}
    goms = append(goms, gom)
    return gom
}

func (gom *TGom) Commit(commit string) *TGom {
    gom.commit = commit
    return gom
}

func (gom *TGom) OS(os ...string) *TGom {
    gom.os = os
    return gom
}

func main() {
    Group("production", "development").
        Gom("github.com/mattn/go-ole").
            Commit("ecb144fb1f2848a24ebfdadf8e64380406d87206")

    Gom("github.com/mattn/go-ole").
        Commit("ecb144fb1f2848a24ebfdadf8e64380406d87206")

    for _, gom := range goms {
        fmt.Println(gom.name, gom.commit)
    }
}

You might report the nesting issue as a bug in gofmt. Seems to me it should honor indentation.

No, it's not a problem just for go. It's a related issue of identation for Gomfile.
For example, below is a valid indentaion for go.

a := NewSomething().
a.SetAttr("foo", "bar").
    SetAttr("bar", "baz").
    SetAttr("baz", "foo").

But Gomfile have a mean for indentation that different from go.

It's valid but IMO if the design calls for such indention, e.g.

a := NewSomething().
a.SetAttr("foo", "bar").
    SetAttr("bar", "baz").
        SetAttr("baz", "foo")

Then gofmt needs to follow along and not make an assumption about what the significance of that indention is. In this particular case we know that SetAttr() is going to return a so indenting like this doesn't really make sense, but there is no way for gofmt to know that. If could be the SetAttr returned a new object every time instead, then this indention would make perfect sense and gofmt should leave it be.

I like the gpm Godeps format.. https://github.com/pote/gpm

github.com/nu7hatch/gotrail               v0.0.2
github.com/replicon/fast-archiver         v1.02
launchpad.net/gocheck                     r2013.03.03   # Bazaar repositories are supported
code.google.com/p/go.example/hello/...    ae081cd1d6cc  # And so are Mercurial ones

@pkieltyka The format can't define extra package. For example, packages just for windows.

I see.. are people using a :platform directive to specify OS-specific libraries? I thought that would be handled by go's build tool via the standard // +build linux darwin etc

Currently, gom gen gomfile doesn't handle that.

I've also noticed a lot of Go developers using the classic INI format.. it's pretty clean: https://github.com/Unknwon/goconfig

This is what https://github.com/gpmgo/gopm does and it's nice .. their example:

[deps]
github.com/pilu/fresh=
github.com/astaxie/beego=tag:v0.9.0

you could do something like

[deps:windows]

[deps -windows +linux]
or whatever you come up with ..

+1 for something in pure Go

Otherwise something in an standard format that can be written / read with external tooling e.g. JSON, JSON5, CSON, YAML

https://github.com/hashicorp/hcl might also be an acceptable solution?

Awkward discussion. Even if one doesn't know ruby it's still pretty simple to grasp the current syntax. I don't think the proposed syntax changes have much value other than "it should not remind me of ruby".

On the other hand machine readable would be nice - that's a fair point.
It's unfortunate that JSON doesn't allow for comments - but I would still vote for it.

Please no YAML.

I don't see the value from the syntax change unless we could make it more readable, and don't like the idea to introduce another package to parse the configuration which could be done with only few lines of code.

I agree with @jinzhu, changing the Gomfile syntax produces more works with no remarkable value at this time.