You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

自定义Or类型的Functor实例使用as-pattern时类型检查失败

Hey there! Let's figure out why your as-pattern rewrite of the Or Functor instance is failing, and how to fix it.

First, let's recap the working version for reference:

data Or a b = First a | Second b deriving (Eq, Show)

instance Functor (Or a) where
  fmap _ (First x) = First x
  fmap f (Second b) = Second $ f b

The Problem with the As-Pattern Version

When you write:

instance Functor (Or a) where
  fmap _ first@(First _) = first
  fmap f (Second b) = Second $ f b

the type checker throws a fit because of a mismatch between the input and output types of fmap. Let's break down fmap's type for Or a:

fmap :: (b -> c) -> Or a b -> Or a c

The input is an Or a b, and the output needs to be an Or a c (note that b and c are distinct type variables).

When you use first@(First _), the variable first gets inferred as having type Or a b (the input type). But fmap expects you to return an Or a c. Since b and c are separate rigid type variables (they can't be unified unless explicitly allowed), the compiler can't reconcile Or a b with Or a c—hence the error about not being able to match a1 (the compiler's name for b) with b (its name for c).

In the working version, when you write First x on the right-hand side, the compiler knows it can construct an Or a c from the a value x—the First constructor doesn't care about the second type parameter, so it happily builds the correct output type. But when you return first directly, you're trying to reuse the input value's type, which doesn't match what fmap needs to return.

Fixing the As-Pattern Version

You have a couple of clean options here:

  1. Reconstruct the First value (even with the as-pattern)
    You can keep the as-pattern for clarity (if you need to reference the whole value elsewhere) but still rebuild the First constructor to get the right type:

    instance Functor (Or a) where
      fmap _ first@(First x) = First x
      fmap f (Second b) = Second $ f b
    

    This works because First x is inferred as Or a c (the output type), while first is Or a b—the constructor lets us "cast" the value to the correct type by reusing the a payload.

  2. Use a type annotation with ScopedTypeVariables
    If you really want to return first directly, you can use the ScopedTypeVariables extension to explicitly tell the compiler that first can be treated as Or a c:

    {-# LANGUAGE ScopedTypeVariables #-}
    
    data Or a b = First a | Second b deriving (Eq, Show)
    
    instance Functor (Or a) where
      fmap _ (first@(First _) :: Or a c) = first
      fmap f (Second b) = Second $ f b
    

    The type annotation forces the compiler to unify the input's second type parameter with the output's, making the type check pass.

Why This Matters

Haskell's type system treats Or a b and Or a c as distinct types when b ≠ c, even though they share the same First constructor. Reconstructing the First value lets the type system know you intend to produce the output type, whereas returning the input value directly locks you into the input type.

内容的提问来源于stack exchange,提问作者Matthias Braun

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 07:52:48