获取构造函数的名称
Get name of the constructor
我正在尝试找到一种方法来获取我的数据类型构造函数的名称作为字符串
data Test
= Foo
{ a :: Int
, b :: Int
}
| Bar
{ a :: Int
}
| Lol
| Lel String
我正在搜索 name :: Constructor Test -> String
形式的东西,可以像这样使用:
name Lol -- "Lol"
name Foo -- "Foo"
name Lel -- "Lel"
我最接近的结果是:
module Main where
import Data.Typeable
import Data.Data
data Test
= Foo
{ a :: Int
, b :: Int
}
| Bar
{ a :: Int
}
| Lol
| Lel String
deriving (Show, Data, Typeable)
main :: IO ()
main = do
print $ toConstr Lol
print $ toConstr $ Bar undefined
print $ toConstr $ Foo undefined undefined
但是 toConstr
将对象作为参数而不是构造函数除外 :/
请注意,构造函数本身具有类型:
Foo :: Int -> Int -> Test
Bar :: Int -> Test
Lol :: Test
Lel :: Strinng -> Test
所以你要求一个函数name
,它可以接受一个类型与这些"patterns"中的任何一个匹配的构造函数来生成String
。如果您写下 name
的类型签名,它需要类似于:
name :: (a1 -> a2 -> ... -> an -> Test) -> String
或者,如果我们想将它用于任何对象,而不仅仅是 Test
,例如:
name :: (a1 -> a2 -> ... -> an -> finalObject) -> String
其中 a
类型的数量取决于构造函数的数量。
在 Haskell 中没有直接的方法来编写这样的函数。其实在"plain"Haskell是不可能的。然而,通过一些扩展,它可以使用一些类型 class 技巧来完成。
所需的扩展是:
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
想法是为name
函数引入一个类型class:
class Name a where
name :: a -> String
然后引入一个实例来处理 a
仍然需要参数的情况,方法是提供 undefined
以将参数计数减少一个:
instance Name (r -> a) where
name f = name (f undefined)
这个实例将被递归使用。当我们调用 name Foo
时,它将用于将其减少为 name (Foo undefined)
,然后再次用于将其减少为 name (Foo undefined undefined)
。由于这个最终对象与模式 r -> a
不匹配,我们将准备好使用默认实例:
instance Name a where
name = show . toConstr
此代码无法按原样运行。我们需要在适当的地方添加一些约束,并使用 OVERLAPPING
pragma 来处理这些重叠的实例,但是类型 class 及其实例的最终定义是:
class Name a where
name :: a -> String
instance {-# OVERLAPPING #-} Name a => Name (r -> a) where
name f = name (f undefined)
instance (Data a) => Name a where
name = show . toConstr
这很好用:
λ> name Foo
"Foo"
λ> name Bar
"Bar"
λ> name Lol
"Lol"
λ> name Lel
"Lel"
但是,既然您拥有了这个功能,我想您会发现在实际程序中使用它是非常困难的。
无论如何,完整的代码如下。请注意,现代版本的 GHC 不需要 deriving Typeable
,因此您可以将其省略。
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
module Constructor where
import Data.Data
data Test
= Foo
{ a :: Int
, b :: Int
}
| Bar
{ a :: Int
}
| Lol
| Lel String
deriving (Show, Data)
class Name a where
name :: a -> String
instance {-# OVERLAPPING #-} Name a => Name (r -> a) where
name f = name (f undefined)
instance (Data a) => Name a where
name = show . toConstr
main = do
print $ name Foo
print $ name Bar
print $ name Lol
print $ name Lel
我正在尝试找到一种方法来获取我的数据类型构造函数的名称作为字符串
data Test
= Foo
{ a :: Int
, b :: Int
}
| Bar
{ a :: Int
}
| Lol
| Lel String
我正在搜索 name :: Constructor Test -> String
形式的东西,可以像这样使用:
name Lol -- "Lol"
name Foo -- "Foo"
name Lel -- "Lel"
我最接近的结果是:
module Main where
import Data.Typeable
import Data.Data
data Test
= Foo
{ a :: Int
, b :: Int
}
| Bar
{ a :: Int
}
| Lol
| Lel String
deriving (Show, Data, Typeable)
main :: IO ()
main = do
print $ toConstr Lol
print $ toConstr $ Bar undefined
print $ toConstr $ Foo undefined undefined
但是 toConstr
将对象作为参数而不是构造函数除外 :/
请注意,构造函数本身具有类型:
Foo :: Int -> Int -> Test
Bar :: Int -> Test
Lol :: Test
Lel :: Strinng -> Test
所以你要求一个函数name
,它可以接受一个类型与这些"patterns"中的任何一个匹配的构造函数来生成String
。如果您写下 name
的类型签名,它需要类似于:
name :: (a1 -> a2 -> ... -> an -> Test) -> String
或者,如果我们想将它用于任何对象,而不仅仅是 Test
,例如:
name :: (a1 -> a2 -> ... -> an -> finalObject) -> String
其中 a
类型的数量取决于构造函数的数量。
在 Haskell 中没有直接的方法来编写这样的函数。其实在"plain"Haskell是不可能的。然而,通过一些扩展,它可以使用一些类型 class 技巧来完成。
所需的扩展是:
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
想法是为name
函数引入一个类型class:
class Name a where
name :: a -> String
然后引入一个实例来处理 a
仍然需要参数的情况,方法是提供 undefined
以将参数计数减少一个:
instance Name (r -> a) where
name f = name (f undefined)
这个实例将被递归使用。当我们调用 name Foo
时,它将用于将其减少为 name (Foo undefined)
,然后再次用于将其减少为 name (Foo undefined undefined)
。由于这个最终对象与模式 r -> a
不匹配,我们将准备好使用默认实例:
instance Name a where
name = show . toConstr
此代码无法按原样运行。我们需要在适当的地方添加一些约束,并使用 OVERLAPPING
pragma 来处理这些重叠的实例,但是类型 class 及其实例的最终定义是:
class Name a where
name :: a -> String
instance {-# OVERLAPPING #-} Name a => Name (r -> a) where
name f = name (f undefined)
instance (Data a) => Name a where
name = show . toConstr
这很好用:
λ> name Foo
"Foo"
λ> name Bar
"Bar"
λ> name Lol
"Lol"
λ> name Lel
"Lel"
但是,既然您拥有了这个功能,我想您会发现在实际程序中使用它是非常困难的。
无论如何,完整的代码如下。请注意,现代版本的 GHC 不需要 deriving Typeable
,因此您可以将其省略。
{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE UndecidableInstances #-}
module Constructor where
import Data.Data
data Test
= Foo
{ a :: Int
, b :: Int
}
| Bar
{ a :: Int
}
| Lol
| Lel String
deriving (Show, Data)
class Name a where
name :: a -> String
instance {-# OVERLAPPING #-} Name a => Name (r -> a) where
name f = name (f undefined)
instance (Data a) => Name a where
name = show . toConstr
main = do
print $ name Foo
print $ name Bar
print $ name Lol
print $ name Lel