You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何转译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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.05 01:11:32