如何转译Swift不透明类型示例至Haskell?求等价实现
Swift不透明类型示例的Haskell转译与问题解答
Apple Inc.. (2022). 《Opaque Types — The Swift Programming Language (Swift 5.7)》
Swift文档示例
// main :: IO () func main() -> () { let shape = join( Triangle(size: 3), flip(Triangle(size: 3)) ) return print( shape.draw() ) } protocol Shape { func draw() -> String } func flip<T: Shape>(_ shape: T) -> some Shape { return FlippedShape(shape: shape) } func join<T: Shape, U: Shape>(_ top: T, _ bottom: U) -> some Shape { JoinedShape(top: top, bottom: bottom) } struct FlippedShape<T: Shape>: Shape { var shape: T func draw() -> String { let lines = shape.draw().split(separator: "\n") return lines.reversed().joined(separator: "\n") } } struct JoinedShape<T: Shape, U: Shape>: Shape { var top: T var bottom: U func draw() -> String { return top.draw() + "\n" + bottom.draw() } } struct Triangle: Shape { var size: Int func draw() -> String { var result: [String] = [] for length in 1...size { result.append(String(repeating: "*", count: length)) } return result.joined(separator: "\n") } } main()
我的Haskell实现尝试
module Main where import Data.List.Extra class Shape a where draw :: a -> String newtype Triangle = Triangle Int newtype Square = Square Int instance Shape Triangle where draw (Triangle n) = unlines $ take n $ iterate ('*' :) "*" flip :: Shape a => a -> a flip shape = flippedShape shape join :: Shape a => a -> a -> a join top bottom = joinedShape top bottom flippedShape :: Shape a => a -> a flippedShape = unlines . reverse . splitOn "\n" . draw joinedShape :: Shape a => a -> a -> a joinedShape top bottom = draw top <> "\n" <> draw bottom main :: IO () main = print $ Main.join (Triangle 3) (Main.flip (Triangle 3))
问题解答
1. 正确的Haskell转译实现
你的尝试存在类型不匹配问题:flippedShape和joinedShape的实现返回的是String,但签名声明返回符合Shape的类型a,这在Haskell中无法通过编译。正确的转译需要复刻Swift的结构——用包装类型封装翻转和拼接后的形状,并让这些包装类型实现Shape类型类:
module Main where import Data.List (intercalate) -- 对应Swift的Shape协议 class Shape a where draw :: a -> String -- 对应Swift的Triangle结构体 data Triangle = Triangle Int deriving (Show) instance Shape Triangle where draw (Triangle n) = intercalate "\n" [replicate k '*' | k <- [1..n]] -- 对应Swift的FlippedShape结构体 newtype FlippedShape a = FlippedShape a deriving (Show) instance Shape a => Shape (FlippedShape a) where draw (FlippedShape shape) = intercalate "\n" . reverse . lines $ draw shape -- 对应Swift的JoinedShape结构体 data JoinedShape a b = JoinedShape a b deriving (Show) instance (Shape a, Shape b) => Shape (JoinedShape a b) where draw (JoinedShape top bottom) = draw top ++ "\n" ++ draw bottom -- 对应Swift的flip函数 flipShape :: Shape a => a -> FlippedShape a flipShape = FlippedShape -- 对应Swift的join函数 joinShapes :: (Shape a, Shape b) => a -> b -> JoinedShape a b joinShapes = JoinedShape main :: IO () main = putStrLn $ draw $ joinShapes (Triangle 3) (flipShape (Triangle 3))
这个实现完全对齐Swift的逻辑:每个操作返回对应的包装类型,所有类型都实现Shape类型类,最终main函数输出拼接后的形状字符串。
2. Haskell中对应Swift some不透明类型的实现
Swift的some Shape是隐式存在类型——它承诺返回一个符合Shape协议的具体类型,但对外隐藏该类型的细节。在Haskell中,需要通过显式存在类型实现相同效果,需启用GHC扩展:
方式1:自定义存在类型
{-# LANGUAGE ExistentialQuantification #-} module Main where import Data.List (intercalate) class Shape a where draw :: a -> String data Triangle = Triangle Int deriving (Show) instance Shape Triangle where draw (Triangle n) = intercalate "\n" [replicate k '*' | k <- [1..n]] newtype FlippedShape a = FlippedShape a deriving (Show) instance Shape a => Shape (FlippedShape a) where draw (FlippedShape shape) = intercalate "\n" . reverse . lines $ draw shape data JoinedShape a b = JoinedShape a b deriving (Show) instance (Shape a, Shape b) => Shape (JoinedShape a b) where draw (JoinedShape top bottom) = draw top ++ "\n" ++ draw bottom -- 定义存在类型,隐藏具体的Shape实现类型 data AnyShape = forall a. Shape a => AnyShape a instance Shape AnyShape where draw (AnyShape s) = draw s -- 现在flip和join返回AnyShape,对外只暴露Shape接口 flipShape :: Shape a => a -> AnyShape flipShape = AnyShape . FlippedShape joinShapes :: (Shape a, Shape b) => a -> b -> AnyShape joinShapes top bottom = AnyShape $ JoinedShape top bottom main :: IO () main = putStrLn $ draw $ joinShapes (Triangle 3) (flipShape (Triangle 3))
方式2:使用标准库的Some类型
GHC标准库提供了GHC.Some模块封装存在类型,结合ConstraintKinds扩展可以更简洁实现:
{-# LANGUAGE ConstraintKinds #-} {-# LANGUAGE ExistentialQuantification #-} module Main where import Data.List (intercalate) import GHC.Some (Some(..)) class Shape a where draw :: a -> String data Triangle = Triangle Int deriving (Show) instance Shape Triangle where draw (Triangle n) = intercalate "\n" [replicate k '*' | k <- [1..n]] newtype FlippedShape a = FlippedShape a deriving (Show) instance Shape a => Shape (FlippedShape a) where draw (FlippedShape shape) = intercalate "\n" . reverse . lines $ draw shape data JoinedShape a b = JoinedShape a b deriving (Show) instance (Shape a, Shape b) => Shape (JoinedShape a b) where draw (JoinedShape top bottom) = draw top ++ "\n" ++ draw bottom -- 定义符合Shape约束的存在类型 type SomeShape = Some Shape flipShape :: Shape a => a -> SomeShape flipShape = Some . FlippedShape joinShapes :: (Shape a, Shape b) => a -> b -> SomeShape joinShapes top bottom = Some $ JoinedShape top bottom main :: IO () main = putStrLn $ draw (joinShapes (Triangle 3) (flipShape (Triangle 3)) :: SomeShape)
两种方式的核心都是通过存在类型隐藏具体实现类型,只对外暴露Shape接口,和Swift的some语义完全一致。
内容的提问来源于stack exchange,提问作者F. Zer
相关产品推荐
相关产品推荐

