Getting 'a' value from 'Maybe a' return type in Haskell

UnworthyToast picture UnworthyToast · Feb 24, 2015 · Viewed 16.6k times · Source

This is going to be hard to explain because there is a decent amount of background detail about the code as a whole that needs to be known to really know functionally what I'm talking about. But I'll try my best to just get my main point across, and hope that it's enough. Let me know if not and I'll add more information. So:

I have a Haskell function eval :: WExp -> Memory -> WValue with a bunch of different instances of itself for different cases. For now, knowledge about WExp, Memory, and WValue is not relevant. My problem is that, for a specific instance of eval, I am using a lookup function, which takes the parameter of eval (a string in this case) searches a list of key-value pairs for that string. Note that this lookup function is not the one included in the Prelude; it is self-defined within the .hs file. If the string is found, the value associated with it is returned, but if it is not found, Nothing is returned. Because of the Nothing case, the type of lookup is actually Maybe a, where a would be a WValue in this case. Because eval would return a Maybe WValue, the compiler obviously complains that the type is not WValue.

Again, if you need more information on what these other types are, I can provide it. It is just my thought that there might be some kind of general method to extract the a value from any function that returns Maybe a. If not, I guess I'll look elsewhere for solutions :)

Answer

dfeuer picture dfeuer · Feb 25, 2015

Do this

do
   input <- getUserInput
   result <- lookup input structure
   case result of
     Just a -> putStrLn $ "I'm so happy you chose "++show a++"."
     Nothing -> putStrLn $ "So sorry; "++input++" is not a valid option."

Don't do this

do
   input <- getUserInput
   result <- lookup input structure
   case result of
     Just a -> putStrLn $ "I'm so happy you chose "++show a++"."
     Nothing -> error $ input ++ " is not a valid option."

This is bad because your program just goes splat if the user input is wrong.

Really don't do this

There is a function called fromJust that attempts to pull a value out of a Maybe and throws an error if it finds Nothing. It looks like

fromJust :: Maybe a -> a
fromJust (Just a) = a
fromJust Nothing = error "Oops, you goofed up, fool."

This makes it hard to see what went wrong.

And really, really don't do this

But if you want to play with fire, you can try it just for fun. This will attempt to get a value out of a Maybe and crash real hard if it finds Nothing. By "crash real hard" I mean you'll get a segmentation fault if you're lucky, and you'll publish your private keys on the web if you're not.

{-# LANGUAGE GADTs, DataKinds, KindSignatures #-}
{-# OPTIONS_GHC -fno-warn-unused-binds #-}

module Unsafe.FromJust (unsafeFromJust) where

-- Clear sign of bad news
import Unsafe.Coerce (unsafeCoerce)

-- This creates a "closed kind" with types
-- 'JustType and 'NothingType. You could just
-- define datatypes called JustType and NothingType,
-- but this makes the intent clearer.
data MaybeType = JustType | NothingType

data M (t::MaybeType) a where
  -- The order of these constructors must not
  -- be changed, because this type must look,
  -- at runtime, exactly like a Maybe
  N :: M 'NothingType a
  J :: a -> M 'JustType a

-- A safe sort of fromJust for M.
fromJ :: M 'JustType a -> a
fromJ (J a) = a

-- Really, seriously unsafe.
unsafeFromJust :: Maybe a -> a
unsafeFromJust m = fromJ (unsafeCoerce m)