DSL in Go: How to Create a Domain-Specific Language for Real-World Tasks

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

DSL in Go: Turning Your Code into a New Language



Imagine you are writing an order processing system. Every day, managers ask: "Add a 10% discount for all customers from Moscow, but only if the order total is greater than 5000." You dive into the code, fix the if-else, test, deploy. A week later — a new request: "15% discount for VIPs if there are more than three items." Sound familiar?



What if managers could write such rules themselves? Without your involvement. Without the risk of breaking production. This is exactly what domain-specific languages (DSLs) are for — compact languages tailored to a specific task. And Go, with its simplicity and performance, is ideal for creating such DSLs.



In this article, we will explore how to create DSLs in Go — from simple embedded solutions to full-fledged external parsers. You will learn how to automate routine tasks and give users a tool they can understand without programming.



What is a DSL and Why Do You Need It in Go?



A DSL (Domain-Specific Language) is a programming language created for a narrow domain. Unlike general-purpose languages (Go, Python, Java), a DSL solves one task, but does it elegantly.



Real-world examples:

  • SQL — a DSL for working with databases.
  • Regex — a DSL for searching patterns in text.
  • HTML — a DSL for marking up web pages.


In Go, DSLs are used for:

  • Configuration — instead of YAML/JSON, you can write logic in your own language.
  • Business rules — allowing analysts to change logic without programmers.
  • Testing — describing scenarios in a readable form.
  • Data processing — creating transformation pipelines.


Go was chosen for a reason: its compiler is fast, its syntax is concise, and its built-in tools (e.g., text/scanner or go/ast) simplify parsing. But most importantly, Go allows you to create both internal DSLs (embedded into Go's syntax) and external DSLs (with their own syntax and parser).



Internal DSLs in Go: Using the Language's Syntax



Internal DSLs are libraries that look like a separate language but use Go's syntax. They do not require writing a parser: you simply write functions that "chain" together in call chains.



Example: DSL for Discount Rules



Suppose we want rules to look like this:



discount := Rule("VIP discount").    When(City("Moscow")).    When(OrderSum > 5000).    Apply(Discount(10))


How to implement this in Go? Use the Builder pattern and higher-order functions:



package main

import "fmt"

// Rule - rule structuretype Rule struct { Name string Checks []func(order map[string]interface{}) bool Discount int}

// When adds a conditionfunc (r *Rule) When(check func(order map[string]interface{}) bool) *Rule { r.Checks = append(r.Checks, check) return r}

// Apply sets the discountfunc (r *Rule) Apply(discount int) *Rule { r.Discount = discount return r}

// Evaluate checks if the order qualifiesfunc (r *Rule) Evaluate(order map[string]interface{}) bool { for _, check := range r.Checks { if !check(order) { return false } } return true}

// Helper condition functionsfunc City(city string) func(map[string]interface{}) bool { return func(order map[string]interface{}) bool { return order["city"] == city }}

func OrderSum(order map[string]interface{}) int { return order["sum"].(int)}

// Operator > overloading via closurefunc GreaterThan(a func(map[string]interface{}) int, b

Blogs

Book Recommendations