louthy/language-ext · error · Exception
Ord attribute should have a struct type that derives from…
Error message
Ord attribute should have a struct type that derives from LanguageExt.Traits.Ord<> passed as its argument
What it means
OrdAttribute.Validate is applied to the type passed to the [Ord] attribute and throws unless that type implements LanguageExt.Traits.Ord<T>. The [Ord] attribute on a Record must point at a struct that provides ordering semantics for the record; anything else makes the generated comparison invalid.
Solutions
- Make the type passed to [Ord] a struct that implements LanguageExt.Traits.Ord<T> for your record type T.
- If migrating, update the comparer to the LanguageExt.Traits.Ord<T> interface (new namespace and shape) instead of the legacy Ord<T>.
- Ensure the comparer is a struct, not a class, as required by the attribute contract.
Example fix
// before
[Ord(typeof(MyOrd))] // MyOrd implements old LanguageExt.Ord<Rec>
public partial record Rec(int X);
// after
public readonly struct MyOrd : LanguageExt.Traits.Ord<Rec>
{
public int Compare(Rec x, Rec y) => x.X.CompareTo(y.X);
}
[Ord(typeof(MyOrd))]
public partial record Rec(int X); Defensive patterns
Strategy: validation
Validate before calling
// before applying the attribute
typeof(MyOrd).GetInterfaces().Any(i =>
i.IsGenericType && i.GetGenericTypeDefinition() == typeof(LanguageExt.Traits.Ord<>)); Type guard
static bool IsValidOrdComparer<T>(T t) => t is LanguageExt.Traits.Ord<RecordType>;
Prevention
- Always define Ord comparers as structs implementing LanguageExt.Traits.Ord<T>.
- After LanguageExt upgrades, re-check comparer interfaces compile against Traits.
- Add a unit test that constructs an attributed record to surface attribute validation early.
When it happens
Trigger: Decorating a record with [Ord(typeof(X))] where X does not implement LanguageExt.Traits.Ord<T> (or implements the old LanguageExt.Ord<T> interface), so the interface check for a name starting with "LanguageExt.Traits.Ord`1" fails.
Common situations: Migrating from LanguageExt 3.x/4.x (where Ord lived in a different namespace) to 5.x Traits namespace; pointing the attribute at a class instead of a struct; pointing it at the record itself instead of a separate comparer type.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- Hashable attribute should have a struct type that derives…
- Eq attribute should have a struct type that derives from…
- Don't use Equals - use either RecordType
- Don't use Equals - use either RecordType
- s
AI-assisted analysis of louthy/language-ext@2f0e362824 (2026-09-15).
Data as JSON: /api/errors/daa505af210e999d.
Report an issue: GitHub.
Appendix: source
Thrown at LanguageExt.Core/DataTypes/Record/Attributes.cs:41
.ImplementedInterfaces
.AsIterable()
.Exists(i => i.ToString().StartsWith("LanguageExt.Traits.Eq`1")))
{
throw new Exception("Eq attribute should have a struct type that derives from LanguageExt.Traits.Eq<> passed as its argument");
}
}
}
public class OrdAttribute : Attribute
{
public OrdAttribute(Type type)
{
if (!type.GetTypeInfo()
.ImplementedInterfaces
.AsIterable()
.Exists(i => i.ToString().StartsWith("LanguageExt.Traits.Ord`1")))
{
throw new Exception("Ord attribute should have a struct type that derives from LanguageExt.Traits.Ord<> passed as its argument");
}
}
}
public class HashableAttribute : Attribute
{
public HashableAttribute(Type type)
{
if (!type.GetTypeInfo()
.ImplementedInterfaces
.AsIterable()
.Exists(i => i.ToString().StartsWith("LanguageExt.Traits.Hashable`1")))
{
throw new Exception("Hashable attribute should have a struct type that derives from LanguageExt.Traits.Hashable<> passed as its argument");
}
}
}
View on GitHub (pinned to 2f0e362824)