简体   繁体   English

基本类型的模式匹配

[英]Pattern matching on basic types

Here is what I want: 这是我想要的:

f n = case n of
  String -> "string"
  _ -> "other"

Or something. 或者其他的东西。 I want f to map to the literal "string" if called with a string, "other" otherwise. 如果要用字符串调用,我希望f映射到原义的“字符串”,否则应映射为“其他”。 This cannot be as hard as I am making it. 这不能像我正在做的那么难。

If you're coming from JS and Python it's understandable you think that something that this should be possible – after all, in those languages a type is essentially a particular property of a value. 如果您来自JS和Python,那是可以理解的,您认为这应该可行–毕竟,在这些语言中,类型本质上是值的特定属性。 But that's not at all how the Haskell type system works! 但这根本不是Haskell类型系统如何工作的! In fact, types and values belong to completely separate “universes”: types only exist at compile time, values only at runtime. 实际上,类型和值属于完全独立的“ Universe”:类型仅在编译时存在,值仅在运行时存在。 It's still true that every value belongs to a type, but this information is already completely resolved at compile-time. 确实每个值都属于一个类型,但是此信息在编译时已被完全解析。 At runtime it is generally not necessary to dispatch on types, because the compiler has already done that earlier, and it can never be possible that a value of type other than the valid one is ever passed in. So this would always be redundant. 在运行时,通常不必分派类型,因为编译器已经提前进行了分配,并且永远不可能传入有效值以外其他类型的值。因此,这总是多余的。 In fact the type information is removed before the program is run. 实际上在运行程序之前, 已删除类型信息
So in the direct sense, what you're trying to accomplish is indeed impossible. 因此,从直接的意义上说,您要实现的目标确实是不可能的。 Or, cynically speaking, it's trivial : 或者,愤世嫉俗地说,这很简单

f :: String -> String
f _ = "string"

Generally, if you find yourself wanting to runtime-dispatch on types it's a sign that you should probably have started out with a variant type in the first place: 通常,如果您发现自己想在类型上进行运行时分派,则表明您可能首先应该使用变体类型:

data StringOrInt = It'sAString String | It'sAnInt Int

f :: StringOrInt -> String
f (It'sAString _) = "string"
f (It'sAnInt _) = "int"

...ideally with more descriptive, application-specific names. ...理想地使用更具描述性的,特定于应用程序的名称。

That said, it is actually possible to have “dynamically typed” values in Haskell, like in Python. 这就是说,它实际上可以在Haskell的“动态类型”的值,比如在Python。 You just need to request the wrapper that contains the type information which the compiler would otherwise erase before the runtime: 您只需要请求包含类型信息的包装器,否则编译器会在运行时删除这些信息:

{-# LANGUAGE TypeFamilies #-}

import Data.Dynamic
import Type.Reflection

f :: Dynamic -> String
f (Dynamic t _)
   = case eqTypeRep t (typeOf "string") of
       Just HRefl -> "string"
       _          -> "other"

Example usage: 用法示例:

main = do
   putStrLn . f $ toDyn "bla"
   putStrLn . f $ toDyn True
   putStrLn . f $ toDyn 'y'

yielding 屈服

string
other
other

A more elegant way of writing f than with the awkward eqTypeRep construct would be 与笨拙的eqTypeRep构造相比,写f更优雅的方式是

f :: Dynamic -> String
f s = case fromDynamic s :: Maybe String of
       Just _ -> "string"
       _      -> "other"

In general, ad-hoc polymorphism and type introspection like this in Haskell is frowned upon. 通常,在Haskell中像这样的临时多态性和类型自省是不受欢迎的。 It's possible anyway, but a little bit fragile: 仍然有可能,但是有点脆弱:

{-# LANGUAGE FlexibleInstances #-}

class F a where
  f :: a -> String

instance F String where
  f n = "string"

instance {-# OVERLAPPABLE #-} F other where
  f n = "other"

Alternatively, you can use Typeable , which is less fragile, at the expense of needing an instance from the caller: 另外,您可以使用不太脆弱的Typeable ,但需要从调用方获取实例:

import Data.Typeable

f :: Typeable a => a -> String
f n = if typeOf n == typeOf "" then "string" else "other"

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM