lets try to port it to Haskell literally..
data Person = Person { fname :: String
, lname :: String
}
In your Python code, any Person object can perform an action greet() that does some IO which gets translated to Haskell as:
greet :: Person -> IO ()
greet person = putStrLn $ (fname person) ++ " " ++ (lname person)
then you would write main as:
main :: IO ()
main = greet $ Person "Ben" "Tennyson"
Now, what's wrong in doing it that way ? - Purity.
Haskell idioms provides ways to keep pure and impure code separate.
lets try to do it Haskell way...
Create a Category Human whose elements could be a Person/Student/Employee or whatever who all share a common action greet()
class Human a where
greet :: a -> String
We can also create a Show instance for our Person type:
instance Show Person where
show person = (fname person) ++ " " ++ (lname person)
then create a Human instance for Person implementing greet() action:
instance Human Person where
greet person = "Hello, " ++ (show person)
main :: IO ()
main = putStrLn . greet $ Person "Ben" "Tennyson"
-- λ main
-- Hello, Ben Tennyson