It's the behavior inconsistency caused by any protocol expected?

With Swift like that:

protocol EmptyProtocol { }

func printValue<T>(_ value: T) {
    print("value: \(value), type: \(type(of: value))")
}

struct MyStruct: EmptyProtocol {
    let name: String
}

class MyClass: EmptyProtocol {
    let id: Int
    init(id: Int) { self.id = id }
}

let items: [EmptyProtocol] = [
    MyStruct(name: "Swift"),
    MyClass(id: 42)
]

printValue(MyStruct(name: "Swift"))
printValue(MyClass(id: 42))

for item in items {
    printValue(item)
}

And the result is:

value: MyStruct(name: "Swift"), type: MyStruct

value: output.MyClass, type: MyClass

value: MyStruct(name: "Swift"), type: EmptyProtocol

value: output.MyClass, type: EmptyProtocol

Which means we loose type information for EmptyProtocol
But with Haskell,


import Data.Typeable
import System.IO
import GHC.IO.Encoding (setLocaleEncoding)

-- 1. Define an empty typeclass
class EmptyProtocol a

-- 2. Define some types and implement the typeclass
data MyStruct = MyStruct String deriving (Show, Typeable)
data MyClass  = MyClass Int    deriving (Show, Typeable)

instance EmptyProtocol MyStruct
instance EmptyProtocol MyClass

-- 3. Generic function: print value and type
printValue :: (Show a, Typeable a) => a -> IO ()
printValue x = putStrLn $ "Value: " ++ show x ++ ", Type: " ++ show (typeOf x)

-- 4. Existential wrapper for heterogeneous list
data AnyValue = forall a. (Show a, Typeable a) => AnyValue a

castToAny :: (Show a, Typeable a) => a -> AnyValue
castToAny = AnyValue

printAny :: AnyValue -> IO ()
printAny (AnyValue x) = printValue x

-- 5. Main function
main :: IO ()
main = do
    let items = [castToAny (MyStruct "Haskell"), castToAny (MyClass 42)]
    mapM_ printAny items

We can get right type information

Value: MyStruct "Haskell", Type: MyStruct
Value: MyClass 42, Type: MyClass

It’s such behavior expected?

Yes, this is expected behavior when the type parameter is a protocol. You can get the behavior you’re seeking by casting to Any:

print("value: \(value), type: \(type(of: value as Any))")
1 Like