Go Interop

GoLisp compiles to Go. Every Go package is available — the interop syntax is lightweight and predictable.

Calling Go Methods

Prefix a method name with . to call it on a value:

(.Year t)              ; → t.Year()
(.Format t "2006-01-02")  ; → t.Format("2006-01-02")
(.Close conn)          ; → conn.Close()
(.String err)          ; → err.Error() ... actually err.String()

This is the syntax for Go methods you don't own. For your own defmethod methods, just call them by name.

Accessing Fields

Prefix with .- to read a struct field:

(.-Timeout client)     ; → client.Timeout
(.-Status resp)        ; → resp.Status

Struct Literals

Use TypeName. followed by a map to create a Go struct:

(http/Client. {})
(http/Client. {:Timeout (* 30 time/Second)})
(sync/Mutex. {})
(net/TCPAddr. {:IP net/IPv4loopback :Port 8080})

Qualified Calls

Call functions from any Go package using package/FunctionName:

(time/now)                         ; → time.Now()
(strings/has-prefix s "https://")  ; → strings.HasPrefix(s, "https://")
(strings/to-upper s)               ; → strings.ToUpper(s)
(math/sqrt 2.0)                    ; → math.Sqrt(2.0)
(fmt/sprintf "%.2f" 3.14)          ; → fmt.Sprintf("%.2f", 3.14)
(os/exit 1)                        ; → os.Exit(1)

Package Constants and Variables

Access package-level constants and variables with /:

math/pi             ; → math.Pi
time/Second         ; → time.Second
time/Millisecond    ; → time.Millisecond
os/stdin            ; → os.Stdin
io/EOF              ; → io.EOF

Type Assertions

Use as to assert a value's type:

(as *Circle val)        ; → val.(*Circle)
(as string raw)         ; → raw.(string)
(as error iface)        ; → iface.(error)

Importing External Go Packages

Declare imports in ns:

(ns main
  (:import [github.com/google/uuid]
           [github.com/jackc/pgx/v5]))

(defn new-id [] -> string
  (uuid/new-string))

(defn connect [url string] -> [any error]
  (pgx/connect (ctx/background) url))

Add the dependency to glisp.mod:

module github.com/myuser/myapp

go-require (
  github.com/google/uuid v1.6.0
  github.com/jackc/pgx/v5 v5.7.2
)

Then fetch it:

glisp get -go github.com/google/uuid@v1.6.0

Common Patterns

Time:

(let [now (time/now)
      year (.Year now)
      formatted (.Format now "2006-01-02")]
  (println year formatted))

HTTP client:

(let [client (http/Client. {:Timeout (* 10 time/Second)})]
  (if-err [resp err] (.Get client "https://example.com")
    (println "error:" err)
    (println "status:" (.-StatusCode resp))))

Sorting with a custom comparator:

(sort/slice items (fn [i int j int] -> bool
  (< (:priority (nth items i)) (:priority (nth items j)))))

doto — Fluent Configuration

doto evaluates a value once, runs a sequence of side-effecting steps on it, and returns the value. A (.method args) step threads the value as the receiver, so builder-style APIs read as a chain:

(doto (new-box)
  (.Add "a")
  (.Add "b")
  (.Show))    ; returns the box

A bare (fn args) step threads the value as the first argument instead. Either way, doto returns the original object — handy for "build, configure, hand back" patterns without a throwaway let.

Numeric Auto-Coercion

Values typed as any — map lookups, untyped parameters, range variables — coerce automatically in arithmetic and comparisons. No explicit cast is needed:

(defn total [m] -> int
  (+ (get m "a") (get m "b")))    ; map values are `any`; result coerces to int

Integer-ness is preserved when no float is involved. Typed numeric code stays native (no coercion overhead), and explicit int / float64 remain available when you want to pin a conversion.

Auto-Imported Packages

These packages are imported automatically when used — no ns declaration needed:

fmt, os, strings, strconv, sort, math, time, sync