让StringWrapper1
和StringWrapper2
成为包裹字符串的两种类型(即newtype StringWrapper1 = StringWrapper1 String
和newtype StringWrapper2 = StringWrapper2
)。
现在假设我们正在尝试从StringWrapper1
到StringWrapper2
制作一个函数。
funcWrapper :: StringWrapper1 -> StringWrapper2
一方面,我们希望明确表示我们传入此函数的内容是StringWrapper1
,因此我们不希望仅将StringWrapper1
视为{{的同义词同义词1}}(这会导致错误,正如我自己的经验可以证明的那样)。另一方面,当从概念上构建函数时,我们仍然在某种程度上考虑String
s。我们想要做的是首先构建String
,这不是我们经常包装和解包类型:
func
然后,我们使用func :: String -> String
来构建func
:
funcWrapper
问题/疑问:这是惯用的吗?使用funcWrapper :: StringWrapper1 -> StringWrapper2
funcWrapper (StringWrapper1 str) = StringWrapper2 (func str)
和func
不断复制每个函数似乎很尴尬。 Haskell是否提供了其他一些我不知道的方法?或者我应该只使用类型同义词?
答案 0 :(得分:5)
正如其他人所说,你应该确保这是你想要做的事情(参见左下角的评论)。如果是,则可以使用coerce
from the standard library在具有相同运行时表示的类型之间进行转换:
func :: String -> String
func = ...
...
funcWrapper :: StringWrapper1 -> StringWrapper2
funcWrapper = coerce func
答案 1 :(得分:3)
首先,你应该考虑leftaroundabout的评论,并确保newytpes真正有意义。也就是说,这种包装和展开确实是日常用品,但你可以使它更方便。一种方法是利用字符串包装器是单形仿函数(与Functor
s(多态)相反),因此您可以编写映射函数,例如:
mapWrapper1 :: (String -> String) -> StringWrapper1 -> StringWrapper1
mapWrapper1 f (StringWrapper1 str) = StringWrapper1 (f str)
mapWrapper2 :: (String -> String) -> StringWrapper2 -> StringWrapper2
mapWrapper2 f (StringWrapper2 str) = StringWrapper2 (f str)
这种模式的一个众所周知的概括是来自 mono-traversable 包的MonoFunctor
类。
在两个包装器之间定义转换函数也很容易(用花哨的术语,我们可以说这是两个函子之间的自然转换):
rewrap1as2 :: StringWrapper1 -> StringWrapper2
rewrap1as2 (StringWrapper1 str) = StringWrapper2 str
(rewrap1as2
可以简单地从coerce
Data.Coerce
实施。有关详细信息,请参阅David Young's answer。)
然后可以根据这些更基本的函数来定义user2297560's answer中的wrap
:
mapAndRewrap1as2 :: (String -> String) -> StringWrapper1 -> StringWrapper2
mapAndRewrap1as2 f = rewrap1as2 . mapWrapper1 f
如果您想要更简洁的内容,您可能会感谢newtype包或equivalent Iso
s provided by lens。然而,这可能值得一个单独的答案。
答案 2 :(得分:2)
为什么不编写一个包装任何其他函数的函数?
wrap :: (String -> String) -> StringWrapper1 -> StringWrapper2
wrap f (StringWrapper1 str) = StringWrapper2 (f str)
这会将String -> String
提升为StringWrapper1 -> StringWrapper2
。
答案 3 :(得分:1)
使用newtype-generics包,可以编写类似
的内容{-# language DeriveGeneric #-}
module Main where
import GHC.Generics
import Control.Newtype (Newtype,over)
newtype StringWrapper1 = StringWrapper1 String deriving Generic
instance Newtype StringWrapper1
newtype StringWrapper2 = StringWrapper2 String deriving Generic
instance Newtype StringWrapper2
func :: String -> String
func = undefined
funcWrapper :: StringWrapper1 -> StringWrapper2
funcWrapper = over StringWrapper1 func
我不会定义包装函数,而是在每个站点使用over
。