Raven in 60 seconds
Raven brings expression-oriented code and explicit state modeling to .NET. If you write C#, the records, decimal arithmetic, and console call below will look familiar. Focus on how the quote's possible outcomes carry their own data and how the caller handles them.
import System.Console.*
record Shipment(Id: int, Weight: decimal)
union QuoteResult {
case Quoted(amount: decimal)
case Rejected(reason: string)
}
func Quote(shipment: Shipment) -> QuoteResult {
if shipment.Weight <= 0 {
return .Rejected("Weight must be positive")
}
let amount = 12.50m + shipment.Weight * 1.75m
return .Quoted(amount)
}
let shipment = Shipment(42, 3.5m)
let message = match Quote(shipment) {
.Quoted(let amount) => "Quote: $amount"
.Rejected(let reason) => "Cannot quote: $reason"
}
WriteLine(message)
Quoted carries an amount; Rejected carries a reason. Each outcome has the
data it needs, and match makes the handling visible in one place.
A few Raven spellings carry the rest of the example:
letdeclares a binding that cannot be reassigned;varallows reassignment..Quotedand.Rejecteduse the type supplied by their context.let amountcaptures a case's payload. The binding keyword makes capture explicit, distinct from comparing with an existing value.funcdeclares a function that can live directly in a namespace.
The surrounding .NET APIs remain available in the same program.
Expected failure is data
Raven uses Optional<T> for a value that may be absent and Result<T, E> for an
operation that may succeed or fail in an expected way.
func ParsePort(text: string) -> Result<int, string> {
return int.Parse(text) match {
Ok(let port) when port > 0 => Ok(port)
Ok(_) => Failure("Port must be positive")
Failure(_) => Failure("Port must be a number")
}
}
Raven projects the known framework method int.Parse(string) to a Result
whose error channel preserves its framework exception types, so match handles
its expected failures directly.
The try expression remains available for genuinely throwing APIs, and the
propagation operator ? can return an error from the current function when no
local handling is needed.
Keep familiar .NET design where it fits
Use a class when identity, mutable state, lifecycle, or open polymorphism is part of the model. Use records and unions for value-shaped and closed-domain data. Raven does not require choosing between functional and object-oriented programming for an entire application.