You could do something like this (DISCLAIMER: I have not checked the laws of the relevant typeclasses, and the presence of a string in the constructor for the exception in Alternative makes me wonder if it is lawful). Scala's heterogeneous andThen is covered by fmap; its homogeneous andThen / compose are covered by the >>> / <<< from Category; orElse is covered by <|>; lift is runToMaybe.
However, without a deep compiler integration such as exists in Scala, the pattern incompleteness warnings will interact poorly with this. Haskell only has module-level pragmas for these things, and you won't want to just indiscriminately turn them off in any module where you declare inexhaustive functions, or you may get nasty surprises. Depending on your usecase, you may find optics more idiomatic and less problematic; you can have the boilerplate generated for you through Template Haskell.
(Note: I called it Inexhaustive because PartialFunction is something of a misnomer, in that it implies that Function is total. But Scala has no termination or positivity checkers, so the compiler is not actually able to talk about totality; so you get this weird situation where a function that is not a partial function is just a "regular" Function, whereas you should be able to call it a "total Function". The question here is not partially or totality, which is a broader idea, but inexhaustivity of pattern matches.)
{-# LANGUAGE TypeApplications #-}
module Inexhaustive
( Inexhaustive, inexhaustive
, runToMaybe, isDefinedAt
) where
import Prelude hiding ((.), id)
import Control.Applicative
import Control.Exception
import Control.Category
import Data.Maybe
import System.IO.Unsafe (unsafePerformIO)
newtype Inexhaustive a b = Inexhaustive (a -> b)
inexhaustive :: (a -> b) -> Inexhaustive a b
inexhaustive = Inexhaustive
runToMaybe :: Inexhaustive a b -> a -> Maybe b
runToMaybe (Inexhaustive f) x =
let io = fmap Just $ evaluate $ f x
in unsafePerformIO $ catch @PatternMatchFail io (\_ -> return Nothing)
isDefinedAt :: Inexhaustive a b -> a -> Bool
isDefinedAt f = isJust . runToMaybe f
instance Functor (Inexhaustive z) where
fmap f (Inexhaustive g) = inexhaustive (f . g)
instance Applicative (Inexhaustive z) where
pure x = inexhaustive (const x)
(Inexhaustive zab) <*> (Inexhaustive za) = Inexhaustive (\z -> zab z $ za z)
instance Alternative (Inexhaustive z) where
empty = inexhaustive (\_ -> throw $ PatternMatchFail "inexhaustive empty")
f <|> g =
inexhaustive $ \x ->
case runToMaybe f x <|> runToMaybe g x of
Just y -> y
instance Category Inexhaustive where
id = inexhaustive id
(Inexhaustive f) . (Inexhaustive g) = Inexhaustive (f . g)