首先,我定义以下数据类型
Route::get('/','SearchController@index');
Route::get('/search','SearchController@search');
然后我想实现一个函数,该函数映射到具有以下类型签名的<!DOCTYPE html>
<html>
<head>
<meta id="token" name="_token" content="{{ csrf_token() }}">
<title>Live Search</title>
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
</head>
<body>
<div class="container">
<div class="row">
<div class="panel panel-default">
<div class="panel-heading">
<h3>Products info </h3>
</div>
<div class="panel-body">
<div class="form-group">
<input type="text" class="form-controller" id="search" name="search"></input>
</div>
<table class="table table-bordered table-hover">
<thead>
<tr>
<th>ID</th>
<th>Product Name</th>
<th>Description</th>
<th>Price</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
<script type="text/javascript">
$('#search').on('keyup',function(){
$value=$(this).val();
$.ajax({
type : 'get',
url : '{{URL::to('search')}}',
data:{'search':$value},
success:function(data){
$('tbody').html(data);
}
});
});
</script>
<script type="text/javascript">
$.ajaxSetup({ headers: { 'csrftoken' : '{{ csrf_token() }}' } });
</script>
</body>
</html>
:
data Supply s a = S (Stream s -> (a, Stream s))
data Stream a = Cons a (Stream a)
这是我的实现:(可以毫无问题地编译)
Supply
然后,当我尝试为mapSupply :: (a -> b) -> Supply s a -> Supply s b
内定义的名为mapSupply :: (a -> b) -> Supply s a -> Supply s b
mapSupply mapFunc (S supFuncA) = S supFuncB where
supFuncB strm = let (la, strms) = supFuncA strm in
((mapFunc la), strms)
的辅助函数的类型签名写下问题时,我遇到了一个问题。
supFuncB
的类型签名非常简单,应为:
mapSupply
但是,当我尝试在代码中添加类型签名时,出现了编译器错误。代码看起来像这样
supFuncB
然后编译器抱怨:
supFuncB :: Stream s -> (b, Stream s)
我对Haskell还是陌生的,我不明白为什么编译会失败?如果我将其添加到代码中,那么正确的类型签名应该是什么。
答案 0 :(得分:0)
从头开始,解决方案是打开ScopedTypeVariables
并在forall
签名中使用显式mapSupply
,如下所示:
{-# LANGUAGE ScopedTypeVariables #-} -- Put this at the top of your file.
mapSupply :: forall a b s. (a -> b) -> Supply s a -> Supply s b
mapSupply mapFunc (S supFuncA) = S supFuncB where
supFuncB :: Stream s -> (b, Stream s)
supFuncB strm = let (la, strms) = supFuncA strm in
((mapFunc la), strms)
接下来是为什么这样做的解释。
当您这样写签名时:
mapSupply :: (a -> b) -> Supply s a -> Supply s b
GHC实际上看到了这一点:
mapSupply :: forall a b s. (a -> b) -> Supply s a -> Supply s b
通常可以保留为隐式的forall
表示a
,b
和s
可以是任何东西-mapSupply
是 polymorphic 函数,因此使用它的人可以自由选择三个类型变量的任何具体类型。明确地编写forall
,第二个定义如下:
mapSupply :: forall a b s. (a -> b) -> Supply s a -> Supply s b
mapSupply mapFunc (S supFuncA) = S supFuncB where
supFuncB :: forall s b. Stream s -> (b, Stream s)
supFuncB strm = let (la, strms) = supFuncA strm in
((mapFunc la), strms)
据此,a
中的b
,s
和mapSupply
可以是任何东西,s
和b
也是一样在supFuncB
中。不过,这是一个问题。例如,定义涉及strms
,其类型为s
...,但由于您正在使用s
而显示的supFuncA
是 not < / em>来自supFuncB
签名的一个,而不是来自mapSupply
签名的一个。正如我之前指出的,虽然s
中的mapSupply
可以是任何东西,但是一旦您实际上使用s
选择了mapSupply
,s
supFuncB
中的内容必须与之匹配。就是这样,forall
签名中的supFuncB
不合适,因为其类型变量实际上不能是任何东西。更容易看出我们是否重命名了supFuncB
中的类型变量,以使它们的名称与mapSupply
中的类型变量不冲突(鉴于forall
,这应该是一个有效的做法)那个):
mapSupply :: forall a b s. (a -> b) -> Supply s a -> Supply s b
mapSupply mapFunc (S supFuncA) = S supFuncB where
supFuncB :: forall s1 b1. Stream s1 -> (b1, Stream s1)
supFuncB strm = let (la, strms) = supFuncA strm in
((mapFunc la), strms)
(GHC在内部执行此操作,这说明了为什么您收到的错误消息中提到了s1
类型的变量。)
仅由于添加到supFuncB
的签名(引入了隐式forall
)而发生此问题。没有签名,GHC可以通过不对supFuncB
中的类型进行泛化来完成您想要的工作-在这种情况下,它不是多态的,而是monomorphic in the type variables a
,{{1 }}和b
,它们是从s
借来的。使用mapSupply
扩展名可以在为ScopedTypeVariables
编写类型签名时恢复该行为。启用该功能后,对签名中的类型变量使用显式supFuncB
会使对应定义中具有相同名称的任何类型变量都引用同一事物(只要它们不在{ {1}}签名)。换句话说,这样做可以在相应定义范围内的任何地方引用外部签名中的变量,从而证明扩展名的正确性。