Last updated:

|Edit this page

Hog is the coolest programming language in the world (we're biased).

It is being used to build our CDP product, which you can follow along with in GitHub.

Note: Hog shouldn't be confused with HogQL, our SQL-like query language used inside PostHog. If you're looking to query data in PostHog, see those docs.



Hog comments start with //. You can also use SQL style comments with -- or C++ style multi line blocks with /*.

// Hog comments start with //
-- You can also use SQL style comments with --
/* or C++ style multi line
blocks */


Use := to assign a value to a variable because = is just equals in SQL and HogQL.

// assign 12 to myVar
let myVar := 12
myVar := 13
myVar := myVar + 1


On top of standard comparisons, like, ilike, not like, and not ilike work.

let myVar := 12
print(myVar = 12 or myVar < 10) // prints true
print(myVar < 12 and myVar > 12) // prints false
let string := 'mystring'
print(string ilike '%str%') // prints true


Compares strings against regex patterns. =~ matches exactly, =~* matches case insensitively, !~ does not match, and !~* does not match case insensitively.

print('string' =~ 'i.g$') // true
print('string' !~ 'i.g$') // false
print('string' =~* 'I.G$') // true, case insensitive
print('string' !~* 'I.G$') // false, case insensitive


Supports both dot notation and bracket notation.

Arrays in Hog (and HogQL) are 1-indexed!

let myArray := [1,2,3]
print(myArray.2) // prints 2
print(myArray[2]) // prints 2


Supports both dot notation and bracket notation.

Tuples in Hog (and HogQL) are 1-indexed!

let myTuple := (1,2,3)
print(myTuple.2) // prints 2
print(myTuple[2]) // prints 2


You must use single quotes for object keys and values.

let myObject := {'key': 'value'}
print(myObject.key) // prints 'value'
print(myObject['key']) // prints 'value'
print(myObject?.this?.is?.not?.found) // prints 'null'
print(myObject?.['this']?.['is']?.not?.found) // prints 'null'


Strings must always start end end with a single quote. Includes f-string support.

let str := 'string'
print(str || ' world') // prints 'string world', SQL concat
print(f'hello {str}') // prints 'hello string'
print(f'hello {f'{str} world'}') // prints 'hello string world'


Can only be defined at the top scope. See Hog's standard library for a list of built-in functions.

fn addNumbers(num1, num2) {
let newNum := num1 + num2
return newNum
print(addNumbers(1, 2))


let a := 3
if (a > 0) {

Ternary operations

print(a < 2 ? 'small' : 'big')


let a := null
print(a ?? 'is null') // prints 'is null'

While loop

let i := 0
while(i < 3) {
print(i) // prints 0, 1, 2
i := i + 1

For loop

for(let i := 0; i < 3; i := i + 1) {
print(i) // prints 0, 1, 2

For-in loop

let arr = ['banana', 'tomato', 'potato']
for (let food in arr) {
let obj = {'banana': 3, 'tomato': 5, 'potato': 6}
for (let food, value in arr) {
print(food, value)

Hog's standard library

Hog's standard library includes the following functions and will expand. To see the the most update-to-date list, check the Python VM's stl/__init__.py file.

Type conversion

  • toString(arg: any): string
  • toUUID(arg: any): UUID
  • toInt(arg: any): int
  • toFloat(arg: any): float
  • toDate(arg: string | int): Date
  • toDateTime(arg: string | int): DateTime
  • tuple(...args: any[]): tuple


  • ifNull(value: any, alternative: any)

String functions

  • print(...args: any[])
  • concat(...args: string[]): string
  • match(arg: string, regex: string): boolean
  • length(arg: string): int
  • empty(arg: string): boolean
  • notEmpty(arg: string): boolean
  • lower(arg: string): string
  • upper(arg: string): string
  • reverse(arg: string): string
  • trim(arg: string, char?: string): string
  • trimLeft(arg: string, char?: string): string
  • trimRight(arg: string, char?: string): string
  • splitByString(separator: string, str: string, maxParts?: int): string[]
  • jsonParse(arg: string): any
  • jsonStringify(arg: object): string
  • base64Encode(arg: string): string
  • base64Decode(arg: string): string
  • tryBase64Decode(arg: string): string
  • encodeURLComponent(arg: string): string
  • decodeURLComponent(arg: string): string
  • replaceOne(arg: string, needle: string, replacement: string): string
  • replaceAll(arg: string, needle: string, replacement: string): string
  • generateUUIDv4(): string

Objects and arrays

  • length(arg: any[] | object): int
  • empty(arg: any[] | object): boolean
  • notEmpty(arg: any[] | object): boolean
  • keys(arg: any[] | object): string[]
  • vaues(arg: any[] | object): string[]
  • arrayPushBack(arr: any[], value: any): any[]
  • arrayPushFront(arr: any[], value: any): any[]
  • arrayPopBack(arr: any[]): any[]
  • arrayPopFront(arr: any[]): any[]
  • arraySort(arr: any[]): any[]
  • arrayReverse(arr: any[]): any[]
  • arrayReverseSort(arr: any[]): any[]
  • arrayStringConcat(arr: any[], separator?: string): string

Date functions

  • now(): DateTime
  • toUnixTimestamp(input: DateTime | Date | string, zone?: string): float
  • fromUnixTimestamp(input: number): DateTime
  • toUnixTimestampMilli(input: DateTime | Date | string, zone?: string): float
  • fromUnixTimestampMilli(input: int | float): DateTime
  • toTimeZone(input: DateTime, zone: string): DateTime | Date
  • toDate(input: string | int | float): Date
  • toDateTime(input: string | int | float, zone?: string): DateTime
  • formatDateTime(input: DateTime, format: string, zone?: string): string - we use use the ClickHouse formatDateTime syntax.
  • toInt(arg: any): int - Converts arg to a 64-bit integer. Converts Dates into days from epoch, and DateTimes into seconds from epoch
  • toFloat(arg: any): float - Converts arg to a 64-bit float. Converts Dates into days from epoch, and DateTimes into seconds from epoch
  • toDate(arg: string | int): Date - arg must be a string YYYY-MM-DD or a Unix timestamp in seconds
  • toDateTime(arg: string | int): DateTime - arg must be an ISO timestamp string or a Unix timestamp in seconds

Cryptographic functions

  • md5Hex(arg: string): string
  • sha256Hex(arg: string): string
  • sha256HmacChainHex(arg: string[]): string

Running Hog locally

To run Hog, first, you need to clone and set up PostHog locally. The repo has VMs to run the source code and complied bytecode as well as example files. The default VM relies on PostHog's Python dependencies, but we also have a Typescript VM that relies on those dependencies.

Once you have PostHog set up, go into the repo and run bin/hog with a .hog file.

cd posthog
bin/hog hogvm/__tests__/mandelbrot.hog

You can add the --debug flag to step through and see the stack trace.

Compiling Hog

You can compile a .hog file to a .hoge executable with bin/hoge.

bin/hoge hogvm/__tests__/mandelbrot.hog

You can then run the complied .hoge file automatically with bin/hog.

bin/hog hogvm/__tests__/mandelbrot.hoge


Was this page useful?

Next article

Self-host PostHog

This page covers our free, open-source Docker compose deployment , which is available under a MIT license without guarantee . We continue to develop this version, but some premium features are only available on PostHog Cloud. We do not provide support for this "Hobby" version. We no longer support paid, open-source deployments and it is no longer possible to buy licenses for self-hosted versions - we instead recommend migrating to PostHog Cloud . Our MIT-licensed Docker compose…

Read next article