Haskell - 类型和 if 语句

Haskell - types and if statements

有没有什么好的方法可以利用类型信息来选择做不同的事情?

例如,这无效 Haskell,但我不明白为什么不能:

tostring :: (Show b) => b -> String
tostring x = f x where f = if b == String then tail . init . show else show

重要的部分不是得到正确的字符串,而是使用 b 的类型作为在 functionality/functions.

之间切换的方式

我会原样回答问题。 Haskell 在编译期间擦除所有类型信息,主要是出于效率原因。默认情况下,当调用多态函数时,例如f :: a->a,没有可用的类型信息,f 也没有办法知道 a 到底是什么——在这种情况下,f 只能是恒等函数,失败终止或引发错误。

对于需要类型信息的极少数情况,Typeable。具有类型 f :: Typeable a => ... 的多态函数被传递给类型 a 的 run-time 描述,允许它对其进行测试。本质上,Typeable a 约束强制 Haskell 将 运行 时间信息保留到 运行 时间。请注意,在调用站点必须知道此类类型信息——要么是因为 f 是用完全已知的类型调用的,要么是因为 f 是用部分已知的类型调用的(比如 f x x :: Maybe b) 但在范围内有合适的 Typeable 约束(Typeable b,在前面的示例中)。

无论如何,这里有一个例子:

{-# LANGUAGE TypeApplications, ScopedTypeVariables, GADTs #-}

import Data.Typeable

tostring :: forall b. (Show b, Typeable b) => b -> String
tostring x = case eqT @b @String of  -- if b==String
   Just Refl -> x                    -- then
   Nothing   -> show x               -- else

请注意我们如何能够在“then”分支中 return x,因为已知它是 String.

@chi 的回答已经演示了如何使用 Typeable 进行 run-time 类型检查,但我想向我指出,这看起来正是类型类的用途.对于您的示例,唯一的问题是您不喜欢 StringShow 实现:在那种情况下,只需创建您自己的类型类!

{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}

-- The class
class MyShow a where
    myShow :: a -> String

-- The instance for String
-- (The `OVERLAPPING` pragma is required, because
-- otherwise GHC won't know which instance to choose for String)
instance {-# OVERLAPPING #-} MyShow [Char] where
    myShow = tail . init . show

-- For everything that is not a String, just copy the Show instance
instance Show a => MyShow a where
    myShow = show

编辑:正如 leftaroundabout 所指出的,重叠的实例很复杂,可能会导致一些意外行为。查看文档底部的example