Skip to content

Instantly share code, notes, and snippets.

@arialdomartini
Last active July 21, 2026 07:22
Show Gist options
  • Select an option

  • Save arialdomartini/d06a0bb2e7b0d1f2997d3fbea8fbaecd to your computer and use it in GitHub Desktop.

Select an option

Save arialdomartini/d06a0bb2e7b0d1f2997d3fbea8fbaecd to your computer and use it in GitHub Desktop.
cata.cs
using System;
namespace Catamorphisms.RecursiveTypes;
record Book(string Title, decimal Price);
abstract record ChocolateType;
sealed record Black : ChocolateType;
sealed record Milk : ChocolateType;
sealed record SeventyPercent : ChocolateType;
record Chocolate(ChocolateType ChocolateType, decimal Price);
abstract record WrappingPaperStyle;
sealed record HappyBirthday : WrappingPaperStyle;
sealed record HappyHolidays : WrappingPaperStyle;
sealed record SolidColor : WrappingPaperStyle;
abstract record Gift;
sealed record BookGift(Book Book) : Gift;
sealed record ChocolateGift(Chocolate Chocolate) : Gift;
sealed record Wrapped(Gift Gift, WrappingPaperStyle WrappingPaperStyle) : Gift;
sealed record Boxed(Gift Gift) : Gift;
sealed record WithACard(Gift Gift, string Message) : Gift;
static class Prices
{
const decimal WrappedPrice = 0.5m;
const decimal BoxedPrice = 1m;
const decimal WithACardPrice = 2m;
}
static TResult Cata<TResult>(
Func<Book, TResult> fBook,
Func<Chocolate, TResult> fChocolate,
Func<TResult, WrappingPaperStyle, TResult> fWrapped,
Func<TResult, TResult> fBoxed,
Func<TResult, string, TResult> fWithACard,
Gift gift)
{
// let recur = cata fBook fChocolate fWrapped fBoxed fWithACard
TResult Recur(Gift g) => Cata(fBook, fChocolate, fWrapped, fBoxed, fWithACard, g);
return gift switch
{
BookGift b => fBook(b.Book),
ChocolateGift c => fChocolate(c.Chocolate),
Wrapped w => fWrapped(Recur(w.Gift), w.WrappingPaperStyle),
Boxed b => fBoxed(Recur(b.Gift)),
WithACard w => fWithACard(Recur(w.Gift), w.Message),
_ => throw new ArgumentOutOfRangeException(nameof(gift))
};
}
static decimal TotalCost(Gift gift) => Gifts.Cata<decimal>(
fBook: b => b.Price,
fChocolate: c => c.Price,
fWrapped: (inner, _) => inner + Prices.WrappedPrice,
fBoxed: inner => inner + Prices.BoxedPrice,
fWithACard: (inner, _) => inner + Prices.WithACardPrice,
gift);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment