Skip to main content
Version: v2.2.82 (latest)
v2 · Utilities

Bind & Curry Partial Application

Bind and curry functions allow you to fix some arguments of a function, creating specialized versions with fewer parameters.


Overview

Partial application is the technique of fixing some arguments of a function, producing a new function with fewer parameters.

Key Concepts:

  • Bind1st: Fix the first argument
  • Bind2nd: Fix the second argument
  • Currying: Transform multi-argument function into chain of single-argument functions

Partial application is useful for creating specialized versions of generic functions, reducing code duplication.


Bind1st - Fix First Argument

Fix the first argument of a two-argument function:

bind_first.go
import F "github.com/IBM/fp-go/function"

// Original two-argument function
divide := func(a, b int) int {
  return a / b
}

// Fix first argument to 10
divideBy10 := F.Bind1st(divide, 10)

// Now it's a single-argument function
divideBy10(2)  // 5  (10 / 2)
divideBy10(5)  // 2  (10 / 5)
divideBy10(1)  // 10 (10 / 1)

// Equivalent to:
divide(10, 2)  // 5
divide(10, 5)  // 2
divide(10, 1)  // 10

Bind2nd - Fix Second Argument

Fix the second argument of a two-argument function:

bind_second.go
import F "github.com/IBM/fp-go/function"

// Original two-argument function
divide := func(a, b int) int {
  return a / b
}

// Fix second argument to 10
divideTenBy := F.Bind2nd(divide, 10)

// Now it's a single-argument function
divideTenBy(100)  // 10  (100 / 10)
divideTenBy(50)   // 5   (50 / 10)
divideTenBy(20)   // 2   (20 / 10)

// Equivalent to:
divide(100, 10)  // 10
divide(50, 10)   // 5
divide(20, 10)   // 2

String Operations

Create specialized string functions:

bind_strings.go
import (
  F "github.com/IBM/fp-go/function"
  "strings"
)

// Generic string operation
contains := func(haystack, needle string) bool {
  return strings.Contains(haystack, needle)
}

// Create specialized checkers
hasAt := F.Bind2nd(contains, "@")
hasDot := F.Bind2nd(contains, ".")

// Use specialized functions
hasAt("user@example.com")   // true
hasAt("username")           // false

hasDot("file.txt")          // true
hasDot("filename")          // false

// Combine for email validation
isEmail := func(s string) bool {
  return hasAt(s) && hasDot(s)
}

isEmail("user@example.com")  // true
isEmail("invalid")           // false

API Client Example

Create specialized API request functions:

bind_api.go
type APIClient struct {
  BaseURL string
  Token   string
}

type Response struct {
  Status int
  Body   string
}

// Generic request function
func makeRequest(client *APIClient, endpoint string) Response {
  url := client.BaseURL + endpoint
  // Make HTTP request with client.Token
  return Response{Status: 200, Body: "..."}
}

// Create client
client := &APIClient{
  BaseURL: "https://api.example.com",
  Token:   "secret-token",
}

// Create specialized request function
request := F.Bind1st(makeRequest, client)

// Use with different endpoints
users := request("/users")
posts := request("/posts")
comments := request("/comments")

// Much cleaner than:
// makeRequest(client, "/users")
// makeRequest(client, "/posts")
// makeRequest(client, "/comments")

Configuration Pattern

Fix configuration for processing functions:

bind_config.go
type Config struct {
  Env       string
  Debug     bool
  MaxRetries int
}

type Data struct {
  ID   int
  Name string
}

type Result struct {
  Success bool
  Output  string
}

func processWithConfig(config Config, data Data) Result {
  // Process data using config settings
  if config.Debug {
      fmt.Printf("Processing %s in %s
", data.Name, config.Env)
  }
  return Result{Success: true, Output: data.Name}
}

// Create production processor
prodConfig := Config{
  Env:        "production",
  Debug:      false,
  MaxRetries: 3,
}
processInProd := F.Bind1st(processWithConfig, prodConfig)

// Create development processor
devConfig := Config{
  Env:        "development",
  Debug:      true,
  MaxRetries: 1,
}
processInDev := F.Bind1st(processWithConfig, devConfig)

// Process different data with same config
data1 := Data{ID: 1, Name: "Alice"}
data2 := Data{ID: 2, Name: "Bob"}

prodResult1 := processInProd(data1)
prodResult2 := processInProd(data2)

devResult1 := processInDev(data1)
devResult2 := processInDev(data2)

Array Operations

Create specialized array filters and mappers:

bind_array.go
import (
  A "github.com/IBM/fp-go/array"
  F "github.com/IBM/fp-go/function"
)

// Generic comparison function
greaterThan := func(threshold, value int) bool {
  return value > threshold
}

// Create specialized filters
isGreaterThan10 := F.Bind1st(greaterThan, 10)
isGreaterThan100 := F.Bind1st(greaterThan, 100)

numbers := []int{5, 15, 50, 150, 200}

// Filter with specialized predicates
above10 := F.Pipe2(
  numbers,
  A.Filter(isGreaterThan10),
)
// []int{15, 50, 150, 200}

above100 := F.Pipe2(
  numbers,
  A.Filter(isGreaterThan100),
)
// []int{150, 200}

// Generic multiply function
multiply := func(factor, value int) int {
  return factor * value
}

// Create specialized mappers
double := F.Bind1st(multiply, 2)
triple := F.Bind1st(multiply, 3)

doubled := F.Pipe2(numbers, A.Map(double))
// []int{10, 30, 100, 300, 400}

tripled := F.Pipe2(numbers, A.Map(triple))
// []int{15, 45, 150, 450, 600}

Logging and Middleware

Create specialized logging functions:

bind_logging.go
type Logger struct {
  Prefix string
  Level  string
}

func log(logger *Logger, message string) {
  fmt.Printf("[%s] %s: %s
", logger.Level, logger.Prefix, message)
}

// Create specialized loggers
errorLogger := &Logger{Prefix: "API", Level: "ERROR"}
infoLogger := &Logger{Prefix: "API", Level: "INFO"}

logError := F.Bind1st(log, errorLogger)
logInfo := F.Bind1st(log, infoLogger)

// Use specialized loggers
logInfo("Server started")
// [INFO] API: Server started

logError("Connection failed")
// [ERROR] API: Connection failed

logInfo("Request processed")
// [INFO] API: Request processed

API Reference

FunctionTypeDescription
Bind1st[A, B, C](func(A, B) C, A) -> func(B) CFix first argument
Bind2nd[A, B, C](func(A, B) C, B) -> func(A) CFix second argument

Usage Pattern:

// Original function
f := func(a A, b B) C { ... }

// Fix first argument
g := Bind1st(f, valueA) // g(b B) C

// Fix second argument
h := Bind2nd(f, valueB) // h(a A) C

Related Concepts

Common Use Cases:

  • Creating specialized versions of generic functions
  • Configuration injection
  • API client builders
  • Logging and middleware
  • Reducing code duplication

See Also: