haskell 多态类型函数
haskell polymorphic type functions
在 Haskell 中是否可以有一个函数可以采用多态类型并且 return 可以采用多态类型?
例如,我想要一个接受值的函数,如果值是 Foo
类型,则 return 是一个 Int,如果是 Bar
类型,则是一个 String
data Foo = One | Two | Three deriving (Show, Read)
data Bar = This | That | TheOther deriving (Show, Read)
doSomething :: Either Foo Bar -> Either Int String
doSomething var = if (typeOf var) == Int then 123 else "string"
这样的事情可能吗?如果不是,根据类型路由到另一个函数的最佳做法是什么?
首先,您描述的内容和 Either Int String
签名似乎不匹配 - 我会先尝试您描述的内容(根据输入类型选择输出类型):
你可以做一些非常类似于我认为你正在尝试使用类型族的事情:
{-# LANGUAGE TypeFamilies #-}
module SO where
data Foo = One | Two | Three deriving (Show, Read)
data Bar = This | That | TheOther deriving (Show, Read)
class PolyMap k where
type To k :: *
polyMap :: k -> To k
instance PolyMap Foo where
type To Foo = Int
polyMap _ = 123
instance PolyMap Bar where
type To Bar = String
polyMap _ = "string"
示例:
λ> polyMap One
123
λ> polyMap That
"string"
一些解释
我想你想要的是type-mappings/functions(typeOf
没有运行时检查,开箱即用,这会给你一些很好的类型检查支持)和基本上有两种方法(我知道)
- Type Families 我给你看了
- functional dependencies 仙人掌做了什么
两者都为您(以及其他人)提供了一些表达方式:看看我是否得到类型 A 我可以说出某些关联类型 B 必须是什么(Foo -> Int
和 Bar -> String
)
这是一个很深的话题(边界依赖类型;))但我认为 classes 的类型家族并不难理解。
我使用的想法是让 class PolyMap
提供 polyMap
功能(您可以将其命名为您想要的任何名称 - doSomething
,随便什么)并且输出类型取决于使用 To k
映射的输入类型,如实例声明中所述,Int
对应 Foo
和 String
对应 Bar
.
你签名的另一个更简单:
doSomething :: Either Foo Bar -> Either Int String
doSomething (Left _) = Left 123
doSomething (Right _) = Right "string"
示例:
λ> doSomething (Left One)
Left 123
λ> doSomething (Right That)
Right "string"
您可以将 doSomething
放入以域和共域类型为键的类型类中,具有从域到共域的功能依赖性:
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-} -- if one of your `to` really is `String`
class DoSomething from to | from -> to where
doSomething :: from -> to
instance DoSomething Foo Int where -- ...
instance DoSomething Bar String where -- ...
与基于类型族的解决方案相比,这样做的好处是如果codomain也唯一确定域,则可以添加另一个功能依赖to -> from
。我不认为类型族提供了一种很好的建模方式。
在 Haskell 中是否可以有一个函数可以采用多态类型并且 return 可以采用多态类型?
例如,我想要一个接受值的函数,如果值是 Foo
类型,则 return 是一个 Int,如果是 Bar
类型,则是一个 String
data Foo = One | Two | Three deriving (Show, Read)
data Bar = This | That | TheOther deriving (Show, Read)
doSomething :: Either Foo Bar -> Either Int String
doSomething var = if (typeOf var) == Int then 123 else "string"
这样的事情可能吗?如果不是,根据类型路由到另一个函数的最佳做法是什么?
首先,您描述的内容和 Either Int String
签名似乎不匹配 - 我会先尝试您描述的内容(根据输入类型选择输出类型):
你可以做一些非常类似于我认为你正在尝试使用类型族的事情:
{-# LANGUAGE TypeFamilies #-}
module SO where
data Foo = One | Two | Three deriving (Show, Read)
data Bar = This | That | TheOther deriving (Show, Read)
class PolyMap k where
type To k :: *
polyMap :: k -> To k
instance PolyMap Foo where
type To Foo = Int
polyMap _ = 123
instance PolyMap Bar where
type To Bar = String
polyMap _ = "string"
示例:
λ> polyMap One
123
λ> polyMap That
"string"
一些解释
我想你想要的是type-mappings/functions(typeOf
没有运行时检查,开箱即用,这会给你一些很好的类型检查支持)和基本上有两种方法(我知道)
- Type Families 我给你看了
- functional dependencies 仙人掌做了什么
两者都为您(以及其他人)提供了一些表达方式:看看我是否得到类型 A 我可以说出某些关联类型 B 必须是什么(Foo -> Int
和 Bar -> String
)
这是一个很深的话题(边界依赖类型;))但我认为 classes 的类型家族并不难理解。
我使用的想法是让 class PolyMap
提供 polyMap
功能(您可以将其命名为您想要的任何名称 - doSomething
,随便什么)并且输出类型取决于使用 To k
映射的输入类型,如实例声明中所述,Int
对应 Foo
和 String
对应 Bar
.
你签名的另一个更简单:
doSomething :: Either Foo Bar -> Either Int String
doSomething (Left _) = Left 123
doSomething (Right _) = Right "string"
示例:
λ> doSomething (Left One)
Left 123
λ> doSomething (Right That)
Right "string"
您可以将 doSomething
放入以域和共域类型为键的类型类中,具有从域到共域的功能依赖性:
{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}
{-# LANGUAGE FlexibleInstances #-} -- if one of your `to` really is `String`
class DoSomething from to | from -> to where
doSomething :: from -> to
instance DoSomething Foo Int where -- ...
instance DoSomething Bar String where -- ...
与基于类型族的解决方案相比,这样做的好处是如果codomain也唯一确定域,则可以添加另一个功能依赖to -> from
。我不认为类型族提供了一种很好的建模方式。