Auto convert type in haskell

陌路散爱 提交于 2020-01-13 11:30:08

问题


I've written some helpful functions to do logical operation. It looks like (a and b or c) `belongs` x.

Thanks to Num, IsList and OverloadedLists, I can have it for integers and lists.

λ> (1 && 2 || 3) `belongs` [2]
False
λ> (1 && 2 || 3) `belongs` [1, 2]
True
λ> (1 && 2 || 3) `belongs` [3]
True

λ> :set -XOverloadedLists
λ> ([1, 2] && [2, 3] || [3, 4]) `contains` 1
False
λ> ([1, 2] && [2, 3] || [3, 4]) `contains` 2
True

I do it this way:

newtype BoolLike a = BoolLike ((a -> Bool) -> Bool)

(&&) :: BoolLike a -> BoolLike a -> BoolLike a
BoolLike f && BoolLike g = BoolLike $ \k -> f k P.&& g k
infixr 3 &&

toBoolLike :: a -> BoolLike a
toBoolLike x = BoolLike $ \k -> k x

belongs :: Eq a => BoolLike a -> [a] -> Bool
belongs (BoolLike f) xs = f (\x -> x `elem` xs)

contains :: Eq a => BoolLike [a] -> a -> Bool
contains (BoolLike f) x = f (\xs -> x `elem` xs)

instance Num a => Num (BoolLike a) where
  fromInteger = toBoolLike . fromInteger

What I am going to do is make it suit for any types, without manually convert a to BoolLike a.

How can I achieve it?

First try:

class IsBool a where
  type BoolItem a
  toBool :: a -> BoolItem a

instance IsBool (BoolLike a) where
  type BoolItem (BoolLike a) = BoolLike a
  toBool = id

instance IsBool a where
  type BoolItem a = BoolLike a
  toBool = toBoolLike

Failed:

Conflicting family instance declarations:
  BoolItem (BoolLike a) -- Defined at BoolLike.hs:54:8
  BoolItem a -- Defined at BoolLike.hs:58:8

回答1:


You could try this

type family BoolItem a where
    BoolItem (BoolLike a) = BoolLike a
    BoolItem a = BoolLike a

class IsBool a where
  toBool :: a -> BoolItem a

instance IsBool (BoolLike a) where
  toBool = id

instance (BoolItem a ~ BoolLike a) => IsBool a where
  toBool = toBoolLike

By moving the type family out of the class you can define it for all types. Then all that remains is restricting one of the instances. Don't forget you'll also need to make that one OVERLAPPABLE




回答2:


This answer probably won't be useful to you, as in all likelihood you have already considered the alternative I am going to suggest and deemed it not enough for your devilish purposes. Still, readers who stumble upon this question might find it useful to know how to achieve something similar to what you were looking for, if not quite as nifty, without type class trickery.

In your plans, a BoolLike a ...

newtype BoolLike a = BoolLike ((a -> Bool) -> Bool)

... consists of a function which produces a Bool result when given an a -> Bool continuation. Your usage examples all boil down to combining the Bool results with (&&) and (||) before supplying the continuation. That can be achieved using the Applicative instance for functions (in this case, (->) (a -> Bool)) and using (&)/flip ($) to promote plain values into (a -> Bool) -> Bool "suspended computations":

GHCi> ((||) <$> ((&&) <$> ($ 1) <*> ($ 2)) <*> ($ 3)) (`elem` [2])
False

That, of course, is not at all tidy to write. However, we can improve things quite a lot by defining:

(<&&>) :: Applicative f => f Bool -> f Bool -> f Bool
x <&&> y = (&&) <$> x <*> y

(<||>) :: Applicative f => f Bool -> f Bool -> f Bool
x <||> y = (||) <$> x <*> y

(For a little library defining them, have a look at control-bool.)

Armed with these, the extra line noise becomes quite lightweight:

GHCi> (($ 1) <&&> ($ 2) <||> ($ 3)) (`elem` [2])
False

This works out of the box for the contains case as well — all that it takes is changing the supplied continuation:

GHCi> (($ [1, 2]) <&&> ($ [2, 3]) <||> ($ [3, 4])) (elem 1)
False

As a final note, it is worth pointing out the contains case can be straightforwardly expressed in terms of intersect and union from Data.List:

GHCi> [1, 2] `intersect` [2, 3] `union` [3, 4] & elem 1
False


来源:https://stackoverflow.com/questions/40316051/auto-convert-type-in-haskell

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!