I took the following code from
Apple Inc.. (2022). "Opaque Types — The Swift Programming Language (Swift 5.7)"
Swift documentation example:
// 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()
I am wondering how can I translate it to Haskell. The end goal is joining a Triangle
datatype with the flipped version of a Triangle
datatype. This is my attempt:
Haskell example:
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))
So, I have two questions:
- How can I translate the Swift example into Haskell ?, and
- What's the Haskell equivalent for
some
(Opaque type) ?
CodePudding user response:
The most direct translation adds a shape data type to supplement the shape type class.
class Shape a where draw :: a -> String
newtype SomeShape = SomeShape String
-- if desired, can make this instance:
instance Shape SomeShape where draw (SomeShape s) = s
flip :: Shape a => a -> SomeShape
flip s = SomeShape (unlines . map reverse . lines $ draw s)
This case is a bit too simple to really capture all the exciting bits of opaque types, but because of that simplicity I would also consider using String
directly (or a difference-list equivalent if efficiency is a concern), with no type class or additional data type.
type Shape = String
triangle :: Int -> String
triangle n = {- ... -}
flip :: Shape -> Shape
flip = unlines . map reverse . lines
joinVertical :: Shape -> Shape -> Shape
joinVertical s1 s2 = s1 "\n" s2