typelead / eta

The Eta Programming Language, a dialect of Haskell on the JVM
https://eta-lang.org
BSD 3-Clause "New" or "Revised" License
2.61k stars 145 forks source link

Proper error message for missing constraints in FFI type variables #497

Open tomshackell opened 7 years ago

tomshackell commented 7 years ago
{-# LANGUAGE MagicHash #-}
module Main where

import Java

-- Map.Entry
data {-# CLASS "java.util.Map$Entry" #-} MapEntry k v =
  MapEntry (Object# (MapEntry k v))
  deriving Class

foreign import java unsafe getKey :: Java (MapEntry k v) k
foreign import java unsafe getValue :: Java (MapEntry k v) v

toTuple :: MapEntry k v -> (k, v)
toTuple e = pureJava $ do
    k <- e <.> getKey
    v <- e <.> getValue
    pure (k, v)

main :: IO ()
main = undefined

Eta version: 0.0.9b1

tomshackell commented 7 years ago

From investigating a bit further, I think this is about Eta not liking the fact that getKey and getValue are defined differently in Java.Collections. If I change the code to

{-# LANGUAGE MagicHash, FlexibleContexts #-}
module Main where

import Java

-- Map.Entry
data {-# CLASS "java.util.Map$Entry" #-} MapEntry k v =
  MapEntry (Object# (MapEntry k v))
  deriving Class

foreign import java unsafe "@interface getKey" getKey
  :: (Extends k Object, Extends v Object)
  => Java (MapEntry k v) k

foreign import java unsafe "@interface getValue" getValue
  :: (Extends k Object, Extends v Object)
  => Java (MapEntry k v) v

toTuple :: (Class k, Class v) => MapEntry k v -> (k, v)
toTuple e = pureJava $ do
    k <- e <.> getKey
    v <- e <.> getValue
    pure (k, v)

main :: IO ()
main = undefined

Then it compiles without a panic.

rahulmutt commented 7 years ago

This panic should be ideally converted to a proper error message. Essentially, if you are using any type variables in your import, you must specify a constraint on it. This constraint is essentially a proof that you can cast from a subclass to a parent class and required as the Eta compiler will do the same work that javac does when compiling generic classes. It's the foundation of how we embed subtyping into Eta just for the FFI but not elsewhere in the language.