[Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
(indexing with TypeRep) This is yet another incidence where Robert Will's ByMaps would be very useful In fact GHC at least *already* generates a unique integer for each TypeRep. A good idea, since it means comparisons can be done in unit time. Thus indexing can be done trivially using this integer as a hash function. ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
On Monday 29 November 2004 11:35, George Russell wrote: (indexing with TypeRep) This is yet another incidence where Robert Will's ByMaps would be very useful In fact GHC at least *already* generates a unique integer for each TypeRep. A good idea, since it means comparisons can be done in unit time. Thus indexing can be done trivially using this integer as a hash function. Yes, I have seen this in the code, too. The Ord and Typeable instances should be trivial. [off topic:] There was a recent discussion about allowing to derive an instance from anywhere at the top-level, and not only in the type definition. This is one more example where such a feature would be very useful. Another related example is the class Typeable itself. It has been noted by others that the current interface is not type safe, since mkTyCon gets an arbitrary string as argument. (Unfortunately this means that GlobalVariables.hs and ExecutionContext.hs aren't really type safe either). Typeable would be completely safe if the only way to declare instances would be to derive them, but this is only practical if it can be done from anywhere outside the data type definition. Can anyone think of a situation where adding a derived instance to an abstract data type breaks one of its invariants? Ben ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
On Mon, Nov 29, 2004 at 11:57:31AM +0100, Benjamin Franksen wrote: Can anyone think of a situation where adding a derived instance to an abstract data type breaks one of its invariants? Yes, I was thinking of this the other day, newtype LessThan5 = LessThen5 Int new x | x 5 = LessThen5 x | otherwise = error not less than five if someone were allowed to do a derive (Enum LessThan5) in another module, then they could break the invarient with toEnum 6 for instance. For safety, one should only be able to remotely derive if all the constructors of the type are in scope as well as the type. However, this is too strong of a constraint for deriving Typeable which does not care about the constructors. It is not clear what the correct thing to do is, perhaps have 2 types of derivable classes, ones which need the constructors and ones which don't? Hmm.. I am sort of of the practically motivated opinion that Typable should be a built-in that everything is automatically an instance of, but I don't know if that is really the right thing to do or just a convinient hack. John -- John Meacham - repetae.netjohn ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
On Friday 26 November 2004 08:39, George Russell wrote: Benjamin Franksen wrote (snipped): What non-standard libraries have I used (that you don't)? OK, but you have to test every element of the dictionary with fromDynamic until you find one with the type you want, which is not a good idea if the dictionary is big. My implementation is equally inefficient now (because TypeRep's have no Ord), but if TypeRep's had Ord or a hashing function (both would be very easy to provide from GHC's implementation) I could make my implementation efficient very easily, while you'd have to completely rewrite yours to get the same effect. [completely off-topic but anyway:] This is yet another incidence where Robert Will's ByMaps would be very useful: http://www.stud.tu-ilmenau.de/~robertw/dessy/fun/principles.html#bymap I am quite astonished that apparently none of the data structure library projects have taken up the idea. Ben ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
On Friday 26 November 2004 08:39, you wrote: Benjamin Franksen wrote (snipped): What non-standard libraries have I used (that you don't)? OK, but you have to test every element of the dictionary with fromDynamic until you find one with the type you want, which is not a good idea if the dictionary is big. My implementation is equally inefficient now (because TypeRep's have no Ord), but if TypeRep's had Ord or a hashing function (both would be very easy to provide from GHC's implementation) I could make my implementation efficient very easily, while you'd have to completely rewrite yours to get the same effect. [completely off-topic but anyway:] This is yet another incidence where Robert Will's ByMaps would be very useful: http://www.stud.tu-ilmenau.de/~robertw/dessy/fun/principles.html#bymap I am quite astonished that apparently none of the data structure library projects have taken up the idea. Ben ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
[for the 4th time moving this discussion to cafe] On Friday 26 November 2004 08:39, you wrote: Benjamin Franksen wrote (snipped): Doesn't that run contrary to Adrian Hey's oneShot example/requirement? Remind me again what Adrian Hey's oneShot example/requirement is ... http://www.haskell.org//pipermail/haskell/2004-November/014766.html [...] Furthermore, I have great difficulty in understanding why different threads need different dictionaries. Could you explain why this is useful, or rather, more useful than a global single dictionary? Consider Data.Unique implemented over lots of processors. If you had a single IORef managed by a single processor used to generate new unique identifiers, there is the danger that that processor will become a bottleneck for the whole system. Much better to have a thread-local or processor-local IORef which generates new identifiers, which you then prepend with a processor tag. I see. Note that currently there exists no Haskell implementation that is able to make use of multiple processors. See http://research.microsoft.com/Users/simonpj/papers/conc-ffi/conc-ffi.ps Having read http://www.haskell.org//pipermail/haskell-cafe/2004-November/007666.html again, as well as your comments above, I tend to agree that withEmptyDict may indeed be useful. However, the situations you describe are somewhat special. They can and should be handled by explicitly calling withEmptyDict. I still can't see any reason why each single Haskell thread should have its own searate dictionary. Contrary, since it is common to use forkIO quite casually, and you expect your actions to do the same thing regardless of which thread calls them, this would be disastrous. IMO GlobalVariables.hs shouldn't be aware of threadIds at all. What non-standard libraries have I used (that you don't)? [...explanation...] I see. Thanks for the explanation. Ben ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
On Friday 26 November 2004 14:12, Benjamin Franksen wrote: I still can't see any reason why each single Haskell thread should have its own searate dictionary. Contrary, since it is common to use forkIO quite casually, and you expect your actions to do the same thing regardless of which thread calls them, this would be disastrous. IMO GlobalVariables.hs shouldn't be aware of threadIds at all. I think I misunderstood your proposal (GlobalVariables.hs). It seems to do what I would expect, if your version of forkIO is used. I thought by inheriting the dictionary you meant working on a new copy, but it does in fact mean using the same dictionary. Sorry for the confusion. Ben ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
On Thursday 25 November 2004 10:02, you wrote: This is funny. When I got no immediate reaction from you, I started implementing it myself. I ended up with something similar. It has less features but is also a lot simpler. This is the interface: initGlobal :: Typeable a = a - IO () getGlobal :: Typeable a = IO a Your implementation is probably much simpler than mine because you don't implement withEmptyDict. I'm really quite keen about withEmptyDict, because one of the MAJOR conceptual problems I have with unsafePerformIO global variables is that you only get one universe, corresponding to the Haskell program. There shouldn't really be a single the Haskell program anyway; Doesn't that run contrary to Adrian Hey's oneShot example/requirement? imagine something like GHC or an operating system written in Haskell which run sub-systems which require their own global variables. Well, that's indeed one major problems with global variables. Sure, you can try to solve it with multiple dictionaries, but that makes understanding what a certain part of the program does even harder. How do I find out what dictionary a write or read to a (no longer global) variable refers to? Furthermore, I have great difficulty in understanding why different threads need different dictionaries. Could you explain why this is useful, or rather, more useful than a global single dictionary? It reminds me of the usual thread-local variables that are offered by most systemlevel thread libraries. I think they put them in there so that they can easily port non-reentrant libraries (i.e. ones that use global variables internally) to a multi-threaded setting without changing their APIs. This approach leads to libraries that are extremely inconvenient and dangerous to use. Their existence is one of the reasons why I have been arguing so much against global variables. Storing (TypeRep,Dynamic) pairs is redundant, since Dynamics already contain their own TypeRep (that is how they are made to work). It is, but I'm not sure if it can be avoided without using stuff not in the standard libraries. What non-standard libraries have I used (that you don't)? Ben ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
Benjamin Franksen wrote (snipped): Doesn't that run contrary to Adrian Hey's oneShot example/requirement? Remind me again what Adrian Hey's oneShot example/requirement is ... Well, that's indeed one major problems with global variables. Sure, you can try to solve it with multiple dictionaries, but that makes understanding what a certain part of the program does even harder. How do I find out what dictionary a write or read to a (no longer global) variable refers to? This seems to me as unnecessary as asking for which memory location it has. Provided the no-longer-global variables act as if they were global within their own universe, there is no problem. The withEmptyDict operator I provide gives you a new universe where everything starts from scratch. It seems to me you have a much bigger problem when you force everything to have global variables, and then want to run multiple copies of a program, only to have them clobber each other's variables. Furthermore, I have great difficulty in understanding why different threads need different dictionaries. Could you explain why this is useful, or rather, more useful than a global single dictionary? Consider Data.Unique implemented over lots of processors. If you had a single IORef managed by a single processor used to generate new unique identifiers, there is the danger that that processor will become a bottleneck for the whole system. Much better to have a thread-local or processor-local IORef which generates new identifiers, which you then prepend with a processor tag. Me (snipped): It is, but I'm not sure if it can be avoided without using stuff not in the standard libraries. Ben: What non-standard libraries have I used (that you don't)? OK, but you have to test every element of the dictionary with fromDynamic until you find one with the type you want, which is not a good idea if the dictionary is big. My implementation is equally inefficient now (because TypeRep's have no Ord), but if TypeRep's had Ord or a hashing function (both would be very easy to provide from GHC's implementation) I could make my implementation efficient very easily, while you'd have to completely rewrite yours to get the same effect. ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
Adrian Hey wrote: Why are top level IORefs any worse than other IORefs (for example)? Because global variables are just BAD. They have been considered bad a long time, it's not a Haskell thing. If you really grok the functional way of doing things there should be *very*, *very* few times you need a global variable. I incredibly suspicious about code that needs it. Having a global variable almost always you have a single copy of some data structure; there is no way to create two of them. I claim that the right way is to have a handle to your object and pass that around. (But I can also be persuaded that there might be exceptions. (I've written a few lines of Haskell and I have used a global variable once, I think.)) -- Lennart ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
Adrian Hey wrote: 4- They already exist (stdin,stout,stderr) and I don't recall anybody ever complaining about this. stdin, stdout, and stderr are not global variables. They are just handles. One possible implementation of handles is as an Int. So stdin is no more a global variable than 0. Of course you need some state associated with the handle, but that state does not have to be a unique global things. You are passing that state around via the IO monad, and there could be multiple versions of it. GHC chooses to implement it differently, but that's a choice. -- Lennart ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
Keean Schupke wrote: Adrian Hey wrote: The first step to solving a problem is to at least recognise that it exists. What is bizarre is that so many folk seem to be in denial over this. Perhaps you would like to show me your solution to the oneShot problem. Why are you unable to give a concrete real world example of why this is necessary then. Even your example of real world hardware that must be initialised once fails! (What if I start two copies of the program?) Indeed. With hardware the solution is to do hdl - openDevice which will succeed the first time and then return busy until closed. Any access to the device must use the hdl. Trying to do without the handle is just shooting yourself in the foot. It might look good at first, but it doesn't scale. -- Lennart ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
On 8 Nov 2004, at 12:23, Lennart Augustsson wrote: Adrian Hey wrote: 4- They already exist (stdin,stout,stderr) and I don't recall anybody ever complaining about this. stdin, stdout, and stderr are not global variables. They are just handles. One possible implementation of handles is as an Int. So stdin is no more a global variable than 0. Of course you need some state associated with the handle, but that state does not have to be a unique global things. You are passing that state around via the IO monad, and there could be multiple versions of it. GHC chooses to implement it differently, but that's a choice. Yes... a lot of the example we have seen here are 'just' handles. newIORef creates handles. Something many programmers would like is the ability to create fresh handles at the toplevel... Jules ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
[posted to haskell-cafe per SLPJ's request] Hi Adrian, I can assure you that for the intended applications of oneShot it is vital that realInit is executed once at most, but the user must [..] So please, no more handwaving arguments about this kind of thing being unnecessary, bad programming style, or whatever.. Please show me a concrete alternative in real Haskell code, other I'm mystified as to why you are insisting others provide real examples when you are not. Can you give one concrete example of an intended application of oneShot, so that we can either propose a concrete Haskell implementation of it, or agree that global variables really are necessary. Hoping to increase the light / heat ratio in this discussion... Cheers, --KW 8-) ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
On Monday 08 Nov 2004 3:57 pm, Keith Wansbrough wrote: [posted to haskell-cafe per SLPJ's request] Hi Adrian, I can assure you that for the intended applications of oneShot it is vital that realInit is executed once at most, but the user must [..] So please, no more handwaving arguments about this kind of thing being unnecessary, bad programming style, or whatever.. Please show me a concrete alternative in real Haskell code, other I'm mystified as to why you are insisting others provide real examples when you are not. Maybe you should read the whole thread. AFAIK I am the only person who has provided a concrete example of anything, and I did so in direct response to a request to do so from Keaan IIRC. Unfortunately my own requests for counter examples showing that there are safer (easier, more elegant or whatever) solutions have been ignored (not that I'm in the least bit surprised by this). Instead all I get is repeated denial of the reality of this problem. The problem is simple enough to restate for anyone who's interested. Provide a simple reliable mechanism to ensure that in a given program run one particular top level IO operation cannot be executed more than once. Can you give one concrete example of an intended application of oneShot, so that we can either propose a concrete Haskell implementation of it, or agree that global variables really are necessary. Any C library which requires an explicit initialisation call before anything in that library can be used (common enough IME). Accidental re-initialisation (e.g. by two independent modules/libraries) will destroy any state currently be used by the libraries existing clients. The need to do this may or may not indicate bad design on the part of the library author. But so what? It just happens to be a fact that must be dealt with from Haskell (in a safe manner preferably). Regards -- Adrian Hey ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
Adrian Hey wrote: The problem is simple enough to restate for anyone who's interested. Provide a simple reliable mechanism to ensure that in a given program run one particular top level IO operation cannot be executed more than once. No language can guarantee this - all I have to do is run 2 copies of the executable at once... or wven sequentially! Keean. ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
Adrian Hey writes: The problem is simple enough to restate for anyone who's interested. Provide a simple reliable mechanism to ensure that in a given program run one particular top level IO operation cannot be executed more than once. Can you give one concrete example of an intended application of oneShot, so that we can either propose a concrete Haskell implementation of it, or agree that global variables really are necessary. Any C library which requires an explicit initialisation call before anything in that library can be used (common enough IME). Accidental re-initialisation (e.g. by two independent modules/libraries) will destroy any state currently be used by the libraries existing clients. Great, thanks, that's just what I was hoping for - I now see the problem you're trying to address. --KW 8-) ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
As a purely practical matter, it seems like the easiest solution (to this particular use case) is to write a small wrapper initializer in C which is idempotent, then use FFI to call the wrapper, rather than calling the initialization directly. This is easy enough to do with a static local variable: void doInit() { static int doneInit = 0; if( !doneInit ) { reallyInit(); doneInit = 1; } } Then your haskell libs can call doInit any number of times, and reallyInit will be called at most once. Since your committed to FFI anyway (calling a C lib is the premise), the wrapper seems a small price to pay. For Haskell-only code, something else would be nice. Keith Wansbrough wrote: Adrian Hey writes: The problem is simple enough to restate for anyone who's interested. Provide a simple reliable mechanism to ensure that in a given program run one particular top level IO operation cannot be executed more than once. Can you give one concrete example of an intended application of oneShot, so that we can either propose a concrete Haskell implementation of it, or agree that global variables really are necessary. Any C library which requires an explicit initialisation call before anything in that library can be used (common enough IME). Accidental re-initialisation (e.g. by two independent modules/libraries) will destroy any state currently be used by the libraries existing clients. Great, thanks, that's just what I was hoping for - I now see the problem you're trying to address. --KW 8-) ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
Just to add a small point... you can see how the 'bad' single context design affects the code that uses it. Because C allows global variables it is possible to write libraries that require once-and-only-once initialisation. In Haskell (without global variables) it is impossible (or at least extreemly hard) to write such librarys. Haskell libraries tend to allow multiple concurrent independent threads of access. Allowing global vars into Haskell would make it easy for coders moving to Haskell from C to carry on coding in a bad style. It seems correcting the problem outside of Haskell and in C is the right approach - as it does not involve making these 'bad' things easier to do in Haskell. Keean. Keean Schupke wrote: Any C library which requires an explicit initialisation call before anything in that library can be used (common enough IME). Accidental re-initialisation (e.g. by two independent modules/libraries) will destroy any state currently be used by the libraries existing clients. The need to do this may or may not indicate bad design on the part of the library author. But so what? It just happens to be a fact that must be dealt with from Haskell (in a safe manner preferably). You are right, the C library that works like this is bad design... any library should really be reentrant, an preferably state free. An example of a well designed C library is the ODBC database connection library, where all the state is stored in opaque handles returned to the user. For 'broken' libraries that cannot support multiple simultaneous contexts, it would be better to use the 'C' FFI based solution suggested by another poster. Ideally you would want to find a library with a better interface - If you tell me the library you wish to use I may be able to suggest a better alternative. Keean. ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
On Monday 08 Nov 2004 12:14 pm, Lennart Augustsson wrote: Adrian Hey wrote: Why are top level IORefs any worse than other IORefs (for example)? Because global variables are just BAD. Who said anything about global? If you really grok the functional way of doing things there should be *very*, *very* few times you need a global variable. *very*, *very* few times is not the same as never. Regards -- Adrian Hey ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
I might really want to call the initialisation twice. If you use global variables, the library can only be initialised once... but what if I really want to use the library twice? With the state in a type passed between functions, you can have multiple different states active at once. Keean. Benjamin Franksen wrote: Yes, whenever possible I would use this approach. Unfortunately, there are libraries (or just modules) that need to do some IO action in order to produce the (A)DT. In this case it _will_ make a difference how often you call it. But then this is just how IO actions are by nature, isn't it? Ben ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
[Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
On Sunday 07 November 2004 17:41, Keean wrote: I might really want to call the initialisation twice. If you use global variables, the library can only be initialised once... but what if I really want to use the library twice? With the state in a type passed between functions, you can have multiple different states active at once. Yes, exactly. Just as you might, in fact, *want* to call putString twice... ;) Ben ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
On Sunday 07 Nov 2004 6:19 pm, Benjamin Franksen wrote: On Sunday 07 November 2004 17:41, Keean wrote: I might really want to call the initialisation twice. If you use global variables, the library can only be initialised once... but what if I really want to use the library twice? With the state in a type passed between functions, you can have multiple different states active at once. Yes, exactly. Just as you might, in fact, *want* to call putString twice... Exactly wrong. I have stated several times that realInit cannot be used more than once, and this fact is the reason for the existance of oneShot. I don't want to get to hung up on this one simple example (heaven knows there are plenty of other possible examples), but I'm still waiting for yourself or Keean to demonstrate a simpler and safer way of ensuring this, not waffle your way out by saying it's unnecessary. I can assure you it is necessary. Oh and while we're at it, perhaps one of you could explain what it is you think is unsafe about the hypothetical top level - bindings we're discussing (I've asked this before too, but no answer has been provided). Are your objections dogmatic, aesthetic, or rational? Do either of you object to the existance of such things as stdout? Or are you happy with their existance and it's just their safe creation you object to? If so, this seems like a strange contradiction to me. Regards -- Adrian Hey ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe
Re: [Haskell-cafe] Re: [Haskell] Re: Global Variables and IO initializers
Adrian Hey wrote: Oh and while we're at it, perhaps one of you could explain what it is you think is unsafe about the hypothetical top level - bindings we're discussing (I've asked this before too, but no answer has been provided). Are your objections dogmatic, aesthetic, or rational? Do either of you object to the existance of such things as stdout? Or are you happy with their existance and it's just their safe creation you object to? If so, this seems like a strange contradiction to me. Just because something is possible does not make it desireable. There may be certain extreme examples that really do require this kind of thing - but I would have thought that if the code could be refactored not to require it then that would be better. Keean. ___ Haskell-Cafe mailing list [EMAIL PROTECTED] http://www.haskell.org/mailman/listinfo/haskell-cafe