{"action":"created","issue":{"url":"https://api.github.com/repos/haskell/c2hs/issues/130","labels_url":"https://api.github.com/repos/haskell/c2hs/issues/130/labels{/name}","comments_url":"https://api.github.com/repos/haskell/c2hs/issues/130/comments","events_url":"https://api.github.com/repos/haskell/c2hs/issues/130/events","html_url":"https://github.com/haskell/c2hs/issues/130","id":64564168,"number":130,"title":"Incorrect wrapping of pure functions","user":{"login":"kolmodin","id":157261,"avatar_url":"https://avatars.githubusercontent.com/u/157261?v=3","gravatar_id":"","url":"https://api.github.com/users/kolmodin","html_url":"https://github.com/kolmodin","followers_url":"https://api.github.com/users/kolmodin/followers","following_url":"https://api.github.com/users/kolmodin/following{/other_user}","gists_url":"https://api.github.com/users/kolmodin/gists{/gist_id}","starred_url":"https://api.github.com/users/kolmodin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kolmodin/subscriptions","organizations_url":"https://api.github.com/users/kolmodin/orgs","repos_url":"https://api.github.com/users/kolmodin/repos","events_url":"https://api.github.com/users/kolmodin/events{/privacy}","received_events_url":"https://api.github.com/users/kolmodin/received_events","type":"User","site_admin":false},"labels":[],"state":"closed","locked":false,"assignee":null,"milestone":null,"comments":10,"created_at":"2015-03-26T15:52:18Z","updated_at":"2015-04-21T12:27:06Z","closed_at":"2015-04-19T20:23:15Z","body":"Here's a C function;\r\n\r\n```c\r\n// my_add.h\r\nvoid my_add(int *a, int *b, int *result);\r\n```\r\n\r\n```c\r\n// my_add.c\r\n#include \"my_add.h\"\r\n\r\nvoid my_add(int *a, int *b, int *result) {\r\n  *result = *a + *b;\r\n}\r\n```\r\nYou could argue that it's pure, since it reads nothing but the the arguments and then stores the result in the out param.\r\n\r\nI write this Haskell program;\r\n\r\n```haskell\r\n-- myadd.chs\r\nmodule Main where\r\n\r\nimport Foreign.C.Types\r\nimport Foreign.Ptr\r\nimport Foreign.Storable\r\nimport Foreign.Marshal.Alloc\r\nimport Foreign.Marshal.Utils\r\n\r\nimport System.IO.Unsafe(unsafePerformIO)\r\n\r\n#include \"my_add.h\"\r\n\r\nmain = do\r\n  print (myAdd 1 2)\r\n  print =<< myAddIO 1 2\r\n\r\n{#fun pure unsafe my_add as myAdd   {`CInt', `CInt', alloca- `CInt' peek* } -> `()'#}\r\n{#fun      unsafe my_add as myAddIO {`CInt', `CInt', alloca- `CInt' peek* } -> `()'#}\r\n```\r\n\r\nI've wrapped my_add twice, once as a pure function and once in IO.\r\nRunning this function I'd expect that it'd write two lines with the number 3. Alas it does not.\r\n\r\n```bash\r\n $ clang -c my_add.c\r\n $ c2hs myadd.chs\r\n $ ghc --make myadd.hs my_add.o\r\n $ ./myadd\r\n0\r\n3\r\n```\r\n\r\nWhy? Let's have a look at the generated .hs file. (The issue remains even with newer versions of c2hs).\r\n\r\n```haskell\r\n-- GENERATED by C->Haskell Compiler, version 0.16.4 Crystal Seed, 24 Jan 2009 (Haskell)\r\n-- Edit the ORIGNAL .chs file instead!\r\n\r\n\r\n{-# LINE 1 \"myadd.chs\" #-}module Main where\r\n\r\nimport Foreign.C.Types\r\nimport Foreign.Ptr\r\nimport Foreign.Storable\r\nimport Foreign.Marshal.Alloc\r\nimport Foreign.Marshal.Utils\r\n\r\nimport System.IO.Unsafe(unsafePerformIO)\r\n\r\n\r\nmain = do\r\n  print (myAdd 1 2)\r\n  print =<< myAddIO 1 2\r\n\r\nmyAdd :: CInt -> CInt -> CInt\r\nmyAdd a1 a2 =\r\n  unsafePerformIO $\r\n  with a1 $ \\a1' -> \r\n  with a2 $ \\a2' -> \r\n  alloca $ \\a3' -> \r\n  let {res = myAdd'_ a1' a2' a3'} in\r\n  peek  a3'>>= \\a3'' -> \r\n  return (a3'')\r\n{-# LINE 17 \"myadd.chs\" #-}\r\nmyAddIO :: CInt -> CInt -> IO (CInt)\r\nmyAddIO a1 a2 =\r\n  with a1 $ \\a1' -> \r\n  with a2 $ \\a2' -> \r\n  alloca $ \\a3' -> \r\n  myAddIO'_ a1' a2' a3' >>= \\res ->\r\n  peek  a3'>>= \\a3'' -> \r\n  return (a3'')\r\n{-# LINE 18 \"myadd.chs\" #-}\r\n\r\nforeign import ccall unsafe \"myadd.chs.h my_add\"\r\n  myAdd'_ :: ((Ptr CInt) -> ((Ptr CInt) -> ((Ptr CInt) -> ())))\r\n\r\nforeign import ccall unsafe \"myadd.chs.h my_add\"\r\n  myAddIO'_ :: ((Ptr CInt) -> ((Ptr CInt) -> ((Ptr CInt) -> (IO ()))))\r\n\r\n```\r\n\r\nWe see that `myAdd` does a let-binding, but never uses the `res` variable. It simply won't execute the function.\r\nTo fix this, either `myAdd'_` should be in IO like in `myAddIO'_`, or `res` must be forced.\r\nChanging\r\n```let {res = myAdd'_ a1' a2' a3'} in```\r\nto\r\n```let {res = myAdd'_ a1' a2' a3'} in res `seq````\r\nfixes the issue (though requires that seq has been imported - which is true for all modules that imports the Prelude)."},"comment":{"url":"https://api.github.com/repos/haskell/c2hs/issues/comments/94775565","html_url":"https://github.com/haskell/c2hs/issues/130#issuecomment-94775565","issue_url":"https://api.github.com/repos/haskell/c2hs/issues/130","id":94775565,"user":{"login":"kolmodin","id":157261,"avatar_url":"https://avatars.githubusercontent.com/u/157261?v=3","gravatar_id":"","url":"https://api.github.com/users/kolmodin","html_url":"https://github.com/kolmodin","followers_url":"https://api.github.com/users/kolmodin/followers","following_url":"https://api.github.com/users/kolmodin/following{/other_user}","gists_url":"https://api.github.com/users/kolmodin/gists{/gist_id}","starred_url":"https://api.github.com/users/kolmodin/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/kolmodin/subscriptions","organizations_url":"https://api.github.com/users/kolmodin/orgs","repos_url":"https://api.github.com/users/kolmodin/repos","events_url":"https://api.github.com/users/kolmodin/events{/privacy}","received_events_url":"https://api.github.com/users/kolmodin/received_events","type":"User","site_admin":false},"created_at":"2015-04-21T12:27:06Z","updated_at":"2015-04-21T12:27:06Z","body":"(1) was proposing to change so that all foreign imports that c2hs ever generates are IO.\r\nIrregardless whether a `#fun` is marked as `pure` or not, it should be in IO.\r\n\r\nIndeed, `pure` would only mean that the generated Haskell binding would be pure, not the foreign import (which Manuel pointed out are an implementation detail and should not be relied on)."}}