louthy/language-ext · error · Exception
Eq attribute should have a struct type that derives from…
Error message
Eq attribute should have a struct type that derives from LanguageExt.Traits.Eq<> passed as its argument
What it means
The `[Eq]` attribute on a LanguageExt record declares which type provides equality for the record. Its constructor validates that the supplied `Type` implements `LanguageExt.Traits.Eq<>`; if the passed type doesn't implement that interface, it throws `Exception("Eq attribute should have a struct type that derives from LanguageExt.Traits.Eq<> passed as its argument")` at attribute instantiation time.
Solutions
- Make the passed type a struct implementing `LanguageExt.Traits.Eq<TRecord>`: `public readonly struct MyEq : Eq<MyRecord> { ... }`.
- If you intended ordering, use `[Ord(typeof(...))]` with an `Ord<T>` implementation instead.
- Check the LanguageExt version's trait namespace (`LanguageExt.Traits.Eq<>`) matches your provider's interface.
Example fix
// before
public class MyEq { } // no Eq<T>
[Eq(typeof(MyEq))]
public partial record Person(string Name);
// after
public readonly struct MyEq : Eq<Person>
{
public bool Equals(Person x, Person y) => x.Name == y.Name;
public int GetHashCode(Person x) => x.Name?.GetHashCode() ?? 0;
}
[Eq(typeof(MyEq))]
public partial record Person(string Name); Defensive patterns
Strategy: validation
Validate before calling
bool isValidEqProvider(Type t) =>
t.IsValueType &&
t.GetInterfaces().Any(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(LanguageExt.Traits.Eq<>)); Type guard
bool IsValidEqAttributeArg(Type t) =>
t.GetInterfaces().Any(i => i.ToString().StartsWith("LanguageExt.Traits.Eq`1")); Try / catch
try { var attr = new EqAttribute(typeof(MyEq)); }
catch (Exception ex) when (ex.Message.StartsWith("Eq attribute should have")) { /* fix the provider type */ } Prevention
- Always define Eq providers as structs implementing LanguageExt.Traits.Eq<T>.
- Don't reuse Ord providers in [Eq] attributes; keep separate types.
- Add a unit test that instantiates every record attribute to fail fast at CI time.
When it happens
Trigger: Annotating a record with `[Eq(typeof(MyEq))]` where `MyEq` does not implement `Eq<T>` (e.g. it implements `IEquatable<T>`, `EqualityComparer<T>`, or `Ord<T>` instead), or passing a non-struct/wrong type. The exception surfaces when the attribute is constructed/reflected over.
Common situations: Migrating from older LanguageExt `Eq`/`Ord` interfaces to `LanguageExt.Traits.Eq<>` without updating attribute types; copy-pasting an `Ord` provider into an `[Eq]` attribute; typos creating a provider with the wrong base interface.
Related errors
- Ord attribute should have a struct type that derives from…
- Hashable attribute should have a struct type that derives…
- Don't use Equals - use either RecordType
- Don't use Equals - use either RecordType
- Refs can only be written to from within a `sync` transaction
AI-assisted analysis of louthy/language-ext@2f0e362824 (2026-09-15).
Data as JSON: /api/errors/c5981166df920fc8.
Report an issue: GitHub.
Appendix: source
Thrown at LanguageExt.Core/DataTypes/Record/Attributes.cs:27
public class NonHashAttribute : Attribute;
public class NonEqAttribute : Attribute;
public class NonOrdAttribute : Attribute;
public class NonShowAttribute : Attribute;
public class EqAttribute : Attribute
{
public EqAttribute(Type type)
{
if (!type.GetTypeInfo()
.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");
}
}
}
View on GitHub (pinned to 2f0e362824)