开发者

How to type cast

开发者 https://www.devze.com 2022-12-14 08:20 出处:网络
C#: static int F(object x) { return x is string ? 1开发者_如何学JAVA : 2; } Haskell? The tricky bit seems to me that Haskell does not have a root type object.

C#:

static int F(object x)
{
 return x is string ? 1开发者_如何学JAVA : 2;
}

Haskell? The tricky bit seems to me that Haskell does not have a root type object.

Edited: I do not care about converting to string. I want to know how to typecast (for example to see if an object is a Customer or an Order.


In Haskell, all types that allow a conversion to a string instantiate the Show typeclass which provides

show :: Show a => a -> String

So your whole code is nothing but

f x = show x

or

f = show

with the same generic type f :: Show a => a -> String (Forall types a that are conversible to a string, take a value of this type and return a string).

Note that you don't have to do an explicit, run-time type-check like in C#; the generic template is resolved at compile-time. You don't need a polymorphic root type - A cast like in C# would in fact be somewhat complicated and against the language's conception. Instead of allowing arbitrary casts between types, it defined typeclasses for certain meaningful conversions.

Note that compatibility is checked at compile-time:

-- Working
f 1
f "Hallo"
f (1, 2)
f [1, 2, 3]

-- Not working
f (\x -> x + 1) 

In response to your edited question:

As I said before, arbitrary conversions aren't allowed in Haskell (without very very unsafe code). And since Haskell is not object-oriented, there is no inheritance relationship that required any cast. There simply aren't meaningless object values that needed runtime-checking/casting. For expressing alternatives, you'll have to define a union type, a typeclass or use the Either type.

In what case do you encounter an object that is a Customer or an Order? A value of that type is simply nonsensical. Please clarify again.

As to your logger example: You'll need a typeclass:

class Loggable a where
    writeToLog :: a -> IO ()


Dario's right, generally in Haskell you want to create a type class to dispatch on something's type. That being said, there is a type safe way to cast in Haskell. This is part of the Scrap your boilerplate generic programming library that allows you to write the "interesting" parts of manipulations of complex nested data types while SYB fills in the blanks. Brain meltingly awesome. Here's a presentation on it in ppt or html.

Here's what the cast looks like:

cast :: (Typeable a, Typeable b) => a -> Maybe b
ghci> (cast 'a') :: Maybe Char
Just 'a'
ghci> (cast 'a') :: Maybe Bool
Nothing
ghci> (cast True) :: Maybe Bool
Just True


Casting and is/instanceof make sense in languages that feature subtyping. Your question can be answered in several ways:

  • Haskell does not have subtyping and so is/instanceof make no sense in Haskell; there are only explicit conversions. There is no object type from which all types extend.
  • Haskell's type classes offer ad-hoc polymorphism and help in overloading a single name for many conversions. For example (as others have pointed out) show is the overloaded name for converting any type to a String. Type classes can also be used to make certain conversions (seem) implicit.
  • Using type classes, the authors of the generic Scrap Your Boilerplate library have created a safe cast function (also pointed out by others).


The most straightforward way is to use Data.Typeable, as Dario observes, which covers all the standard types, though you need to pass "Deriving Typeable", or otherwise implement typeOf for your own type definitions to be covered. This is not standard Haskell 98, but it is in ghc since 6.2.2.

Your code could be realised:

 stringtyperep :: TypeRep
 stringtyperep = typeOf "somestring"

 F :: (Typeable 'a) =>'a -> Integer
 F x | (typeOf x == stringtyperep) = 1
 F x = 2

In general, OO-style type reflection is better done with generic programming, but this would not be a good example for that.

Additionally, all types in typeclass Typeable can be "cast" into Data.Dynamic.


If you want to have objects that can either be Customers or Orders, then you'd introduce a new datatype "Customer|Order" so that each object carries a type tag saying which it is.

I don't know the Haskell syntax offhand, but in F# it would be

type CustOrOrd =
    | Cust of Customer
    | Ord of Order

let F (x:CustOrOrd) : int =
    match x with
    | Cust c -> 1
    | Ord o -> 2

let thing1 = Cust someCustomer
let thing2 = Ord someOrder
// can call F on thing1 or thing2

More generally, in Haskell if you want to do something like a fixed OO type hierarchy (for a fixed set of highly related types, to be able to treat them as the same type in some cases), you may use a union type as above. On the other hand, for common operations across a variety of unrelated types (like ToString/Show) you might use a Haskell type class.


This is something I've tried before, and I don't think you can do it.

Introduction to Haskell Pure Functions

Haskell takes a very different approach. It starts by performing type analysis with a type checker to understand your program at compile time. The type checker strictly prohibits type casting and does not allow type errors to be ignored. Because types are checked at compile time, and there is no escape from the type checker, Haskell is frequently described as both statically typed and strongly typed.


Define Object yourself

If your use-case is quite simple, it's probably best just to define the Object type yourself, thus:

data Type = String | Integer | List | Bool
    deriving (Eq, Show)

class Object a where
    typeOf :: a -> Type

instance Object String  where typeOf _ = String
instance Object Integer where typeOf _ = Integer
instance Object [a]     where typeOf _ = List
instance Object Bool    where typeOf _ = Bool

f :: (Object a, Num b) => a -> b
f x = if typeOf x == String then 1 else 2

Obviously you'll have to write an instance declaration for every type that you might want to use as an Object in your function that uses typeOf. This could get tricky when you try to define instances for tuples, because in Haskell two tuples are of different types if they don't have the same number of elements, so you might have to write an infinite number of instance declarations, rather like this:

data Type = String | Integer | List | Bool | Tuple
    deriving (Eq, Show)

instance Object ()              where typeOf _ = Tuple
instance Object (a)             where typeOf _ = Tuple
instance Object (a,b)           where typeOf _ = Tuple
instance Object (a,b,c)         where typeOf _ = Tuple
instance Object (a,b,c,d)       where typeOf _ = Tuple
instance Object (a,b,c,d,e)     where typeOf _ = Tuple
instance Object (a,b,c,d,e,f)   where typeOf _ = Tuple
instance Object (a,b,c,d,e,f,g) where typeOf _ = Tuple

Use Data.Typeable

If your usage gets more complicated, you might try Data.Typeable:

import Data.Typeable

f :: (Typeable a, Num b) => a -> b
f x = if typeOf x == typeOf (undefined::String) then 1 else 2

You can replace the (undefined::String) with just "" if you prefer; whichever reads easier for you.


In C# it's likely more efficient not to do the cast:

return x == null ? null : x.ToString();

In Haskell there's the "show" type class, anything that implements the type class can have show called on it and so no casting is needed.

0

精彩评论

暂无评论...
验证码 换一张
取 消