簡體   English   中英

在Haskell中使用newtypes(“包裝類型”)構建函數的慣用方法是什么?

[英]What is the idiomatic way to build functions over newtypes (“wrapped types”) in Haskell?

StringWrapper1StringWrapper2成為包裹字符串的兩種類型(即newtype StringWrapper1 = StringWrapper1 Stringnewtype StringWrapper2 = StringWrapper2 )。

現在假設我們正在嘗試從StringWrapper1StringWrapper2一個函數。

funcWrapper :: StringWrapper1 -> StringWrapper2

一方面,我們希望明確表示我們傳入此函數的是StringWrapper1 ,因此我們不希望僅將StringWrapper1視為String的類型同義詞(這會導致錯誤,因為我自己的經驗可以證明)。 另一方面,當從概念上構建函數時,我們仍然以某種方式思考String 我們想要做的是首先構建func ,而不是我們不斷包裝和解包類型:

func :: String -> String

然后,我們使用func來構建funcWrapper

funcWrapper :: StringWrapper1 -> StringWrapper2
funcWrapper (StringWrapper1 str) = StringWrapper2 (func str)

問題/疑問:這是慣用的嗎? 使用funcfuncWrapper不斷復制每個函數似乎很尷尬。 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的答案 。)

wrapuser2297560的答案然后可以在這些更基本的功能來定義:

mapAndRewrap1as2 :: (String -> String) -> StringWrapper1 -> StringWrapper2
mapAndRewrap1as2 f = rewrap1as2 . mapWrapper1 f

如果你想要更簡潔的東西,你可能會欣賞newtype包,或鏡頭提供等效Iso 然而,這可能值得一個單獨的答案。

為什么不寫一個將包裝任何其他函數的函數?

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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM