[英]What is the idiomatic way to build functions over newtypes (“wrapped types”) in Haskell?
讓StringWrapper1
和StringWrapper2
成為包裹字符串的兩種類型(即newtype StringWrapper1 = StringWrapper1 String
和newtype StringWrapper2 = StringWrapper2
)。
現在假設我們正在嘗試從StringWrapper1
到StringWrapper2
一個函數。
funcWrapper :: StringWrapper1 -> StringWrapper2
一方面,我們希望明確表示我們傳入此函數的是StringWrapper1
,因此我們不希望僅將StringWrapper1
視為String
的類型同義詞(這會導致錯誤,因為我自己的經驗可以證明)。 另一方面,當從概念上構建函數時,我們仍然以某種方式思考String
。 我們想要做的是首先構建func
,而不是我們不斷包裝和解包類型:
func :: String -> String
然后,我們使用func
來構建funcWrapper
:
funcWrapper :: StringWrapper1 -> StringWrapper2
funcWrapper (StringWrapper1 str) = StringWrapper2 (func str)
問題/疑問:這是慣用的嗎? 使用func
和funcWrapper
不斷復制每個函數似乎很尷尬。 Haskell是否提供了其他一些我不知道的方法? 或者我應該只使用類型同義詞?
正如其他人所說,你應該確保這是你想要做的事情(參見leftaroundabout的評論)。 如果是,您可以使用標准庫中的coerce
轉換具有相同運行時表示的類型:
func :: String -> String
func = ...
...
funcWrapper :: StringWrapper1 -> StringWrapper2
funcWrapper = coerce func
首先,你應該考慮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)
這種模式的一個眾所周知的概括是來自單遍歷包的MonoFunctor
類。
在兩個包裝器之間定義轉換函數也很容易(用花哨的術語,我們可以說它是兩個函子之間的自然轉換):
rewrap1as2 :: StringWrapper1 -> StringWrapper2
rewrap1as2 (StringWrapper1 str) = StringWrapper2 str
( rewrap1as2
可以簡單地從Data.Coerce
coerce
Data.Coerce
。有關詳細信息,請參閱David Young的答案 。)
該wrap
由user2297560的答案然后可以在這些更基本的功能來定義:
mapAndRewrap1as2 :: (String -> String) -> StringWrapper1 -> StringWrapper2
mapAndRewrap1as2 f = rewrap1as2 . mapWrapper1 f
為什么不寫一個將包裝任何其他函數的函數?
wrap :: (String -> String) -> StringWrapper1 -> StringWrapper2
wrap f (StringWrapper1 str) = StringWrapper2 (f str)
這會將任何String -> String
提升為StringWrapper1 -> StringWrapper2
。
使用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
在每個站點。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.